##// END OF EJS Templates
Merge pull request #2312 from adamgd/master...
Brian E. Granger -
r8828:c8270060 merge
parent child Browse files
Show More
@@ -1,368 +1,369
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // CodeCell
9 // CodeCell
10 //============================================================================
10 //============================================================================
11 /**
11 /**
12 * An extendable module that provide base functionnality to create cell for notebook.
12 * An extendable module that provide base functionnality to create cell for notebook.
13 * @module IPython
13 * @module IPython
14 * @namespace IPython
14 * @namespace IPython
15 * @submodule CodeCell
15 * @submodule CodeCell
16 */
16 */
17
17
18 var IPython = (function (IPython) {
18 var IPython = (function (IPython) {
19 "use strict";
19 "use strict";
20
20
21 var utils = IPython.utils;
21 var utils = IPython.utils;
22 var key = IPython.utils.keycodes;
22 var key = IPython.utils.keycodes;
23 CodeMirror.modeURL = "/static/codemirror/mode/%N/%N.js";
23 CodeMirror.modeURL = "/static/codemirror/mode/%N/%N.js";
24
24
25 /**
25 /**
26 * A Cell conceived to write code.
26 * A Cell conceived to write code.
27 *
27 *
28 * The kernel doesn't have to be set at creation time, in that case
28 * The kernel doesn't have to be set at creation time, in that case
29 * it will be null and set_kernel has to be called later.
29 * it will be null and set_kernel has to be called later.
30 * @class CodeCell
30 * @class CodeCell
31 * @extends IPython.Cell
31 * @extends IPython.Cell
32 *
32 *
33 * @constructor
33 * @constructor
34 * @param {Object|null} kernel
34 * @param {Object|null} kernel
35 */
35 */
36 var CodeCell = function (kernel) {
36 var CodeCell = function (kernel) {
37 this.kernel = kernel || null;
37 this.kernel = kernel || null;
38 this.code_mirror = null;
38 this.code_mirror = null;
39 this.input_prompt_number = null;
39 this.input_prompt_number = null;
40 this.tooltip_on_tab = true;
40 this.tooltip_on_tab = true;
41 this.collapsed = false;
41 this.collapsed = false;
42 this.default_mode = 'python';
42 this.default_mode = 'python';
43 IPython.Cell.apply(this, arguments);
43 IPython.Cell.apply(this, arguments);
44
44
45 var that = this;
45 var that = this;
46 this.element.focusout(
46 this.element.focusout(
47 function() { that.auto_highlight(); }
47 function() { that.auto_highlight(); }
48 );
48 );
49 };
49 };
50
50
51
51
52 CodeCell.prototype = new IPython.Cell();
52 CodeCell.prototype = new IPython.Cell();
53
53
54 /**
54 /**
55 * @method auto_highlight
55 * @method auto_highlight
56 */
56 */
57 CodeCell.prototype.auto_highlight = function () {
57 CodeCell.prototype.auto_highlight = function () {
58 this._auto_highlight(IPython.config.cell_magic_highlight)
58 this._auto_highlight(IPython.config.cell_magic_highlight)
59 };
59 };
60
60
61 /** @method create_element */
61 /** @method create_element */
62 CodeCell.prototype.create_element = function () {
62 CodeCell.prototype.create_element = function () {
63 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell vbox');
63 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell vbox');
64 cell.attr('tabindex','2');
64 cell.attr('tabindex','2');
65 var input = $('<div></div>').addClass('input hbox');
65 var input = $('<div></div>').addClass('input hbox');
66 input.append($('<div/>').addClass('prompt input_prompt'));
66 input.append($('<div/>').addClass('prompt input_prompt'));
67 var input_area = $('<div/>').addClass('input_area box-flex1');
67 var input_area = $('<div/>').addClass('input_area box-flex1');
68 this.code_mirror = CodeMirror(input_area.get(0), {
68 this.code_mirror = CodeMirror(input_area.get(0), {
69 indentUnit : 4,
69 indentUnit : 4,
70 mode: 'python',
70 mode: 'python',
71 theme: 'ipython',
71 theme: 'ipython',
72 readOnly: this.read_only,
72 readOnly: this.read_only,
73 extraKeys: {"Tab": "indentMore","Shift-Tab" : "indentLess",'Backspace':"delSpaceToPrevTabStop"},
73 extraKeys: {"Tab": "indentMore","Shift-Tab" : "indentLess",'Backspace':"delSpaceToPrevTabStop"},
74 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this)
74 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this),
75 matchBrackets: true
75 });
76 });
76 input.append(input_area);
77 input.append(input_area);
77 var output = $('<div></div>');
78 var output = $('<div></div>');
78 cell.append(input).append(output);
79 cell.append(input).append(output);
79 this.element = cell;
80 this.element = cell;
80 this.output_area = new IPython.OutputArea(output, true);
81 this.output_area = new IPython.OutputArea(output, true);
81
82
82 // construct a completer only if class exist
83 // construct a completer only if class exist
83 // otherwise no print view
84 // otherwise no print view
84 if (IPython.Completer !== undefined)
85 if (IPython.Completer !== undefined)
85 {
86 {
86 this.completer = new IPython.Completer(this);
87 this.completer = new IPython.Completer(this);
87 }
88 }
88 };
89 };
89
90
90 /**
91 /**
91 * This method gets called in CodeMirror's onKeyDown/onKeyPress
92 * This method gets called in CodeMirror's onKeyDown/onKeyPress
92 * handlers and is used to provide custom key handling. Its return
93 * handlers and is used to provide custom key handling. Its return
93 * value is used to determine if CodeMirror should ignore the event:
94 * value is used to determine if CodeMirror should ignore the event:
94 * true = ignore, false = don't ignore.
95 * true = ignore, false = don't ignore.
95 * @method handle_codemirror_keyevent
96 * @method handle_codemirror_keyevent
96 */
97 */
97 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
98 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
98
99
99 if (this.read_only){
100 if (this.read_only){
100 return false;
101 return false;
101 }
102 }
102
103
103 var that = this;
104 var that = this;
104 // whatever key is pressed, first, cancel the tooltip request before
105 // whatever key is pressed, first, cancel the tooltip request before
105 // they are sent, and remove tooltip if any, except for tab again
106 // they are sent, and remove tooltip if any, except for tab again
106 if (event.type === 'keydown' && event.which != key.TAB ) {
107 if (event.type === 'keydown' && event.which != key.TAB ) {
107 IPython.tooltip.remove_and_cancel_tooltip();
108 IPython.tooltip.remove_and_cancel_tooltip();
108 };
109 };
109
110
110 var cur = editor.getCursor();
111 var cur = editor.getCursor();
111 if (event.keyCode === key.ENTER){
112 if (event.keyCode === key.ENTER){
112 this.auto_highlight();
113 this.auto_highlight();
113 }
114 }
114
115
115 if (event.keyCode === key.ENTER && (event.shiftKey || event.ctrlKey)) {
116 if (event.keyCode === key.ENTER && (event.shiftKey || event.ctrlKey)) {
116 // Always ignore shift-enter in CodeMirror as we handle it.
117 // Always ignore shift-enter in CodeMirror as we handle it.
117 return true;
118 return true;
118 } else if (event.which === 40 && event.type === 'keypress' && IPython.tooltip.time_before_tooltip >= 0) {
119 } else if (event.which === 40 && event.type === 'keypress' && IPython.tooltip.time_before_tooltip >= 0) {
119 // triger on keypress (!) otherwise inconsistent event.which depending on plateform
120 // triger on keypress (!) otherwise inconsistent event.which depending on plateform
120 // browser and keyboard layout !
121 // browser and keyboard layout !
121 // Pressing '(' , request tooltip, don't forget to reappend it
122 // Pressing '(' , request tooltip, don't forget to reappend it
122 IPython.tooltip.pending(that);
123 IPython.tooltip.pending(that);
123 } else if (event.which === key.UPARROW && event.type === 'keydown') {
124 } else if (event.which === key.UPARROW && event.type === 'keydown') {
124 // If we are not at the top, let CM handle the up arrow and
125 // If we are not at the top, let CM handle the up arrow and
125 // prevent the global keydown handler from handling it.
126 // prevent the global keydown handler from handling it.
126 if (!that.at_top()) {
127 if (!that.at_top()) {
127 event.stop();
128 event.stop();
128 return false;
129 return false;
129 } else {
130 } else {
130 return true;
131 return true;
131 };
132 };
132 } else if (event.which === key.ESC) {
133 } else if (event.which === key.ESC) {
133 IPython.tooltip.remove_and_cancel_tooltip(true);
134 IPython.tooltip.remove_and_cancel_tooltip(true);
134 return true;
135 return true;
135 } else if (event.which === key.DOWNARROW && event.type === 'keydown') {
136 } else if (event.which === key.DOWNARROW && event.type === 'keydown') {
136 // If we are not at the bottom, let CM handle the down arrow and
137 // If we are not at the bottom, let CM handle the down arrow and
137 // prevent the global keydown handler from handling it.
138 // prevent the global keydown handler from handling it.
138 if (!that.at_bottom()) {
139 if (!that.at_bottom()) {
139 event.stop();
140 event.stop();
140 return false;
141 return false;
141 } else {
142 } else {
142 return true;
143 return true;
143 };
144 };
144 } else if (event.keyCode === key.TAB && event.type == 'keydown') {
145 } else if (event.keyCode === key.TAB && event.type == 'keydown') {
145 // Tab completion.
146 // Tab completion.
146 //Do not trim here because of tooltip
147 //Do not trim here because of tooltip
147 if (editor.somethingSelected()){return false}
148 if (editor.somethingSelected()){return false}
148 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
149 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
149 if (pre_cursor.trim() === "") {
150 if (pre_cursor.trim() === "") {
150 // Don't autocomplete if the part of the line before the cursor
151 // Don't autocomplete if the part of the line before the cursor
151 // is empty. In this case, let CodeMirror handle indentation.
152 // is empty. In this case, let CodeMirror handle indentation.
152 return false;
153 return false;
153 } else if ((pre_cursor.substr(-1) === "("|| pre_cursor.substr(-1) === " ") && that.tooltip_on_tab ) {
154 } else if ((pre_cursor.substr(-1) === "("|| pre_cursor.substr(-1) === " ") && that.tooltip_on_tab ) {
154 IPython.tooltip.request(that);
155 IPython.tooltip.request(that);
155 // Prevent the event from bubbling up.
156 // Prevent the event from bubbling up.
156 event.stop();
157 event.stop();
157 // Prevent CodeMirror from handling the tab.
158 // Prevent CodeMirror from handling the tab.
158 return true;
159 return true;
159 } else {
160 } else {
160 event.stop();
161 event.stop();
161 this.completer.startCompletion();
162 this.completer.startCompletion();
162 return true;
163 return true;
163 };
164 };
164 } else {
165 } else {
165 // keypress/keyup also trigger on TAB press, and we don't want to
166 // keypress/keyup also trigger on TAB press, and we don't want to
166 // use those to disable tab completion.
167 // use those to disable tab completion.
167 return false;
168 return false;
168 };
169 };
169 return false;
170 return false;
170 };
171 };
171
172
172
173
173 // Kernel related calls.
174 // Kernel related calls.
174
175
175 CodeCell.prototype.set_kernel = function (kernel) {
176 CodeCell.prototype.set_kernel = function (kernel) {
176 this.kernel = kernel;
177 this.kernel = kernel;
177 }
178 }
178
179
179 /**
180 /**
180 * Execute current code cell to the kernel
181 * Execute current code cell to the kernel
181 * @method execute
182 * @method execute
182 */
183 */
183 CodeCell.prototype.execute = function () {
184 CodeCell.prototype.execute = function () {
184 this.output_area.clear_output(true, true, true);
185 this.output_area.clear_output(true, true, true);
185 this.set_input_prompt('*');
186 this.set_input_prompt('*');
186 this.element.addClass("running");
187 this.element.addClass("running");
187 var callbacks = {
188 var callbacks = {
188 'execute_reply': $.proxy(this._handle_execute_reply, this),
189 'execute_reply': $.proxy(this._handle_execute_reply, this),
189 'output': $.proxy(this.output_area.handle_output, this.output_area),
190 'output': $.proxy(this.output_area.handle_output, this.output_area),
190 'clear_output': $.proxy(this.output_area.handle_clear_output, this.output_area),
191 'clear_output': $.proxy(this.output_area.handle_clear_output, this.output_area),
191 'set_next_input': $.proxy(this._handle_set_next_input, this)
192 'set_next_input': $.proxy(this._handle_set_next_input, this)
192 };
193 };
193 var msg_id = this.kernel.execute(this.get_text(), callbacks, {silent: false});
194 var msg_id = this.kernel.execute(this.get_text(), callbacks, {silent: false});
194 };
195 };
195
196
196 /**
197 /**
197 * @method _handle_execute_reply
198 * @method _handle_execute_reply
198 * @private
199 * @private
199 */
200 */
200 CodeCell.prototype._handle_execute_reply = function (content) {
201 CodeCell.prototype._handle_execute_reply = function (content) {
201 this.set_input_prompt(content.execution_count);
202 this.set_input_prompt(content.execution_count);
202 this.element.removeClass("running");
203 this.element.removeClass("running");
203 $([IPython.events]).trigger('set_dirty.Notebook', {'value': true});
204 $([IPython.events]).trigger('set_dirty.Notebook', {'value': true});
204 }
205 }
205
206
206 CodeCell.prototype._handle_set_next_input = function (text) {
207 CodeCell.prototype._handle_set_next_input = function (text) {
207 var data = {'cell': this, 'text': text}
208 var data = {'cell': this, 'text': text}
208 $([IPython.events]).trigger('set_next_input.Notebook', data);
209 $([IPython.events]).trigger('set_next_input.Notebook', data);
209 }
210 }
210
211
211 // Basic cell manipulation.
212 // Basic cell manipulation.
212
213
213 CodeCell.prototype.select = function () {
214 CodeCell.prototype.select = function () {
214 IPython.Cell.prototype.select.apply(this);
215 IPython.Cell.prototype.select.apply(this);
215 this.code_mirror.refresh();
216 this.code_mirror.refresh();
216 this.code_mirror.focus();
217 this.code_mirror.focus();
217 this.auto_highlight();
218 this.auto_highlight();
218 // We used to need an additional refresh() after the focus, but
219 // We used to need an additional refresh() after the focus, but
219 // it appears that this has been fixed in CM. This bug would show
220 // it appears that this has been fixed in CM. This bug would show
220 // up on FF when a newly loaded markdown cell was edited.
221 // up on FF when a newly loaded markdown cell was edited.
221 };
222 };
222
223
223
224
224 CodeCell.prototype.select_all = function () {
225 CodeCell.prototype.select_all = function () {
225 var start = {line: 0, ch: 0};
226 var start = {line: 0, ch: 0};
226 var nlines = this.code_mirror.lineCount();
227 var nlines = this.code_mirror.lineCount();
227 var last_line = this.code_mirror.getLine(nlines-1);
228 var last_line = this.code_mirror.getLine(nlines-1);
228 var end = {line: nlines-1, ch: last_line.length};
229 var end = {line: nlines-1, ch: last_line.length};
229 this.code_mirror.setSelection(start, end);
230 this.code_mirror.setSelection(start, end);
230 };
231 };
231
232
232
233
233 CodeCell.prototype.collapse = function () {
234 CodeCell.prototype.collapse = function () {
234 this.collapsed = true;
235 this.collapsed = true;
235 this.output_area.collapse();
236 this.output_area.collapse();
236 };
237 };
237
238
238
239
239 CodeCell.prototype.expand = function () {
240 CodeCell.prototype.expand = function () {
240 this.collapsed = false;
241 this.collapsed = false;
241 this.output_area.expand();
242 this.output_area.expand();
242 };
243 };
243
244
244
245
245 CodeCell.prototype.toggle_output = function () {
246 CodeCell.prototype.toggle_output = function () {
246 this.collapsed = Boolean(1 - this.collapsed);
247 this.collapsed = Boolean(1 - this.collapsed);
247 this.output_area.toggle_output();
248 this.output_area.toggle_output();
248 };
249 };
249
250
250
251
251 CodeCell.prototype.toggle_output_scroll = function () {
252 CodeCell.prototype.toggle_output_scroll = function () {
252 this.output_area.toggle_scroll();
253 this.output_area.toggle_scroll();
253 };
254 };
254
255
255
256
256 CodeCell.input_prompt_classical = function (prompt_value, lines_number) {
257 CodeCell.input_prompt_classical = function (prompt_value, lines_number) {
257 var ns = prompt_value || "&nbsp;";
258 var ns = prompt_value || "&nbsp;";
258 return 'In&nbsp;[' + ns + ']:'
259 return 'In&nbsp;[' + ns + ']:'
259 };
260 };
260
261
261 CodeCell.input_prompt_continuation = function (prompt_value, lines_number) {
262 CodeCell.input_prompt_continuation = function (prompt_value, lines_number) {
262 var html = [CodeCell.input_prompt_classical(prompt_value, lines_number)];
263 var html = [CodeCell.input_prompt_classical(prompt_value, lines_number)];
263 for(var i=1; i < lines_number; i++){html.push(['...:'])};
264 for(var i=1; i < lines_number; i++){html.push(['...:'])};
264 return html.join('</br>')
265 return html.join('</br>')
265 };
266 };
266
267
267 CodeCell.input_prompt_function = CodeCell.input_prompt_classical;
268 CodeCell.input_prompt_function = CodeCell.input_prompt_classical;
268
269
269
270
270 CodeCell.prototype.set_input_prompt = function (number) {
271 CodeCell.prototype.set_input_prompt = function (number) {
271 var nline = 1
272 var nline = 1
272 if( this.code_mirror != undefined) {
273 if( this.code_mirror != undefined) {
273 nline = this.code_mirror.lineCount();
274 nline = this.code_mirror.lineCount();
274 }
275 }
275 this.input_prompt_number = number;
276 this.input_prompt_number = number;
276 var prompt_html = CodeCell.input_prompt_function(this.input_prompt_number, nline);
277 var prompt_html = CodeCell.input_prompt_function(this.input_prompt_number, nline);
277 this.element.find('div.input_prompt').html(prompt_html);
278 this.element.find('div.input_prompt').html(prompt_html);
278 };
279 };
279
280
280
281
281 CodeCell.prototype.clear_input = function () {
282 CodeCell.prototype.clear_input = function () {
282 this.code_mirror.setValue('');
283 this.code_mirror.setValue('');
283 };
284 };
284
285
285
286
286 CodeCell.prototype.get_text = function () {
287 CodeCell.prototype.get_text = function () {
287 return this.code_mirror.getValue();
288 return this.code_mirror.getValue();
288 };
289 };
289
290
290
291
291 CodeCell.prototype.set_text = function (code) {
292 CodeCell.prototype.set_text = function (code) {
292 return this.code_mirror.setValue(code);
293 return this.code_mirror.setValue(code);
293 };
294 };
294
295
295
296
296 CodeCell.prototype.at_top = function () {
297 CodeCell.prototype.at_top = function () {
297 var cursor = this.code_mirror.getCursor();
298 var cursor = this.code_mirror.getCursor();
298 if (cursor.line === 0 && cursor.ch === 0) {
299 if (cursor.line === 0 && cursor.ch === 0) {
299 return true;
300 return true;
300 } else {
301 } else {
301 return false;
302 return false;
302 }
303 }
303 };
304 };
304
305
305
306
306 CodeCell.prototype.at_bottom = function () {
307 CodeCell.prototype.at_bottom = function () {
307 var cursor = this.code_mirror.getCursor();
308 var cursor = this.code_mirror.getCursor();
308 if (cursor.line === (this.code_mirror.lineCount()-1) && cursor.ch === this.code_mirror.getLine(cursor.line).length) {
309 if (cursor.line === (this.code_mirror.lineCount()-1) && cursor.ch === this.code_mirror.getLine(cursor.line).length) {
309 return true;
310 return true;
310 } else {
311 } else {
311 return false;
312 return false;
312 }
313 }
313 };
314 };
314
315
315
316
316 CodeCell.prototype.clear_output = function (stdout, stderr, other) {
317 CodeCell.prototype.clear_output = function (stdout, stderr, other) {
317 this.output_area.clear_output(stdout, stderr, other);
318 this.output_area.clear_output(stdout, stderr, other);
318 };
319 };
319
320
320
321
321 // JSON serialization
322 // JSON serialization
322
323
323 CodeCell.prototype.fromJSON = function (data) {
324 CodeCell.prototype.fromJSON = function (data) {
324 IPython.Cell.prototype.fromJSON.apply(this, arguments);
325 IPython.Cell.prototype.fromJSON.apply(this, arguments);
325 if (data.cell_type === 'code') {
326 if (data.cell_type === 'code') {
326 if (data.input !== undefined) {
327 if (data.input !== undefined) {
327 this.set_text(data.input);
328 this.set_text(data.input);
328 // make this value the starting point, so that we can only undo
329 // make this value the starting point, so that we can only undo
329 // to this state, instead of a blank cell
330 // to this state, instead of a blank cell
330 this.code_mirror.clearHistory();
331 this.code_mirror.clearHistory();
331 this.auto_highlight();
332 this.auto_highlight();
332 }
333 }
333 if (data.prompt_number !== undefined) {
334 if (data.prompt_number !== undefined) {
334 this.set_input_prompt(data.prompt_number);
335 this.set_input_prompt(data.prompt_number);
335 } else {
336 } else {
336 this.set_input_prompt();
337 this.set_input_prompt();
337 };
338 };
338 this.output_area.fromJSON(data.outputs);
339 this.output_area.fromJSON(data.outputs);
339 if (data.collapsed !== undefined) {
340 if (data.collapsed !== undefined) {
340 if (data.collapsed) {
341 if (data.collapsed) {
341 this.collapse();
342 this.collapse();
342 } else {
343 } else {
343 this.expand();
344 this.expand();
344 };
345 };
345 };
346 };
346 };
347 };
347 };
348 };
348
349
349
350
350 CodeCell.prototype.toJSON = function () {
351 CodeCell.prototype.toJSON = function () {
351 var data = IPython.Cell.prototype.toJSON.apply(this);
352 var data = IPython.Cell.prototype.toJSON.apply(this);
352 data.input = this.get_text();
353 data.input = this.get_text();
353 data.cell_type = 'code';
354 data.cell_type = 'code';
354 if (this.input_prompt_number) {
355 if (this.input_prompt_number) {
355 data.prompt_number = this.input_prompt_number;
356 data.prompt_number = this.input_prompt_number;
356 };
357 };
357 var outputs = this.output_area.toJSON();
358 var outputs = this.output_area.toJSON();
358 data.outputs = outputs;
359 data.outputs = outputs;
359 data.language = 'python';
360 data.language = 'python';
360 data.collapsed = this.collapsed;
361 data.collapsed = this.collapsed;
361 return data;
362 return data;
362 };
363 };
363
364
364
365
365 IPython.CodeCell = CodeCell;
366 IPython.CodeCell = CodeCell;
366
367
367 return IPython;
368 return IPython;
368 }(IPython));
369 }(IPython));
General Comments 0
You need to be logged in to leave comments. Login now