##// END OF EJS Templates
more cleaning
Matthias BUSSONNIER -
Show More
@@ -1,644 +1,641
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 var IPython = (function (IPython) {
12 var IPython = (function (IPython) {
13 "use strict";
13 "use strict";
14
14
15 var utils = IPython.utils;
15 var utils = IPython.utils;
16 var key = IPython.utils.keycodes;
16 var key = IPython.utils.keycodes;
17
17
18 var CodeCell = function (notebook) {
18 var CodeCell = function (notebook) {
19 this.code_mirror = null;
19 this.code_mirror = null;
20 this.input_prompt_number = null;
20 this.input_prompt_number = null;
21 this.completion_cursor = null;
22 this.outputs = [];
21 this.outputs = [];
23 this.collapsed = false;
22 this.collapsed = false;
24 this.tooltip_timeout = null;
23 this.tooltip_timeout = null;
25 this.clear_out_timeout = null;
24 this.clear_out_timeout = null;
26 IPython.Cell.apply(this, arguments);
25 IPython.Cell.apply(this, arguments);
27 };
26 };
28
27
29
28
30 CodeCell.prototype = new IPython.Cell();
29 CodeCell.prototype = new IPython.Cell();
31
30
32
31
33 CodeCell.prototype.create_element = function () {
32 CodeCell.prototype.create_element = function () {
34 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell vbox');
33 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell vbox');
35 cell.attr('tabindex','2');
34 cell.attr('tabindex','2');
36 var input = $('<div></div>').addClass('input hbox');
35 var input = $('<div></div>').addClass('input hbox');
37 input.append($('<div/>').addClass('prompt input_prompt'));
36 input.append($('<div/>').addClass('prompt input_prompt'));
38 var input_area = $('<div/>').addClass('input_area box-flex1');
37 var input_area = $('<div/>').addClass('input_area box-flex1');
39 this.code_mirror = CodeMirror(input_area.get(0), {
38 this.code_mirror = CodeMirror(input_area.get(0), {
40 indentUnit : 4,
39 indentUnit : 4,
41 mode: 'python',
40 mode: 'python',
42 theme: 'ipython',
41 theme: 'ipython',
43 readOnly: this.read_only,
42 readOnly: this.read_only,
44 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this)
43 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this)
45 });
44 });
46 input.append(input_area);
45 input.append(input_area);
47 var output = $('<div></div>').addClass('output vbox');
46 var output = $('<div></div>').addClass('output vbox');
48 cell.append(input).append(output);
47 cell.append(input).append(output);
49 this.element = cell;
48 this.element = cell;
50 this.collapse();
49 this.collapse();
51
50
52 // construct a completer
51 // construct a completer
53 this.completer = new IPython.Completer(this);
52 this.completer = new IPython.Completer(this);
54 };
53 };
55
54
56 //TODO, try to diminish the number of parameters.
55 //TODO, try to diminish the number of parameters.
57 CodeCell.prototype.request_tooltip_after_time = function (pre_cursor,time){
56 CodeCell.prototype.request_tooltip_after_time = function (pre_cursor,time){
58 var that = this;
57 var that = this;
59 if (pre_cursor === "" || pre_cursor === "(" ) {
58 if (pre_cursor === "" || pre_cursor === "(" ) {
60 // don't do anything if line beggin with '(' or is empty
59 // don't do anything if line beggin with '(' or is empty
61 } else {
60 } else {
62 // Will set a timer to request tooltip in `time`
61 // Will set a timer to request tooltip in `time`
63 that.tooltip_timeout = setTimeout(function(){
62 that.tooltip_timeout = setTimeout(function(){
64 IPython.notebook.request_tool_tip(that, pre_cursor)
63 IPython.notebook.request_tool_tip(that, pre_cursor)
65 },time);
64 },time);
66 }
65 }
67 };
66 };
68
67
69 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
68 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
70 // This method gets called in CodeMirror's onKeyDown/onKeyPress
69 // This method gets called in CodeMirror's onKeyDown/onKeyPress
71 // handlers and is used to provide custom key handling. Its return
70 // handlers and is used to provide custom key handling. Its return
72 // value is used to determine if CodeMirror should ignore the event:
71 // value is used to determine if CodeMirror should ignore the event:
73 // true = ignore, false = don't ignore.
72 // true = ignore, false = don't ignore.
74
73
75 if (this.read_only){
74 if (this.read_only){
76 return false;
75 return false;
77 }
76 }
78
77
79 // note that we are comparing and setting the time to wait at each key press.
78 // note that we are comparing and setting the time to wait at each key press.
80 // a better wqy might be to generate a new function on each time change and
79 // a better wqy might be to generate a new function on each time change and
81 // assign it to CodeCell.prototype.request_tooltip_after_time
80 // assign it to CodeCell.prototype.request_tooltip_after_time
82 var tooltip_wait_time = this.notebook.time_before_tooltip;
81 var tooltip_wait_time = this.notebook.time_before_tooltip;
83 var tooltip_on_tab = this.notebook.tooltip_on_tab;
82 var tooltip_on_tab = this.notebook.tooltip_on_tab;
84 var that = this;
83 var that = this;
85 // whatever key is pressed, first, cancel the tooltip request before
84 // whatever key is pressed, first, cancel the tooltip request before
86 // they are sent, and remove tooltip if any
85 // they are sent, and remove tooltip if any
87 if(event.type === 'keydown' ) {
86 if(event.type === 'keydown' ) {
88 that.remove_and_cancel_tooltip();
87 that.remove_and_cancel_tooltip();
89 };
88 };
90
89
91
90
92 if (event.keyCode === key.enter && (event.shiftKey || event.ctrlKey)) {
91 if (event.keyCode === key.enter && (event.shiftKey || event.ctrlKey)) {
93 // Always ignore shift-enter in CodeMirror as we handle it.
92 // Always ignore shift-enter in CodeMirror as we handle it.
94 return true;
93 return true;
95 } else if (event.which === 40 && event.type === 'keypress' && tooltip_wait_time >= 0) {
94 } else if (event.which === 40 && event.type === 'keypress' && tooltip_wait_time >= 0) {
96 // triger aon keypress (!) otherwise inconsistent event.which depending on plateform
95 // triger aon keypress (!) otherwise inconsistent event.which depending on plateform
97 // browser and keyboard layout !
96 // browser and keyboard layout !
98 // Pressing '(' , request tooltip, don't forget to reappend it
97 // Pressing '(' , request tooltip, don't forget to reappend it
99 var cursor = editor.getCursor();
98 var cursor = editor.getCursor();
100 var pre_cursor = editor.getRange({line:cursor.line,ch:0},cursor).trim()+'(';
99 var pre_cursor = editor.getRange({line:cursor.line,ch:0},cursor).trim()+'(';
101 that.request_tooltip_after_time(pre_cursor,tooltip_wait_time);
100 that.request_tooltip_after_time(pre_cursor,tooltip_wait_time);
102 } else if (event.which === key.upArrow) {
101 } else if (event.which === key.upArrow) {
103 // If we are not at the top, let CM handle the up arrow and
102 // If we are not at the top, let CM handle the up arrow and
104 // prevent the global keydown handler from handling it.
103 // prevent the global keydown handler from handling it.
105 if (!that.at_top()) {
104 if (!that.at_top()) {
106 event.stop();
105 event.stop();
107 return false;
106 return false;
108 } else {
107 } else {
109 return true;
108 return true;
110 };
109 };
111 } else if (event.which === key.downArrow) {
110 } else if (event.which === key.downArrow) {
112 // If we are not at the bottom, let CM handle the down arrow and
111 // If we are not at the bottom, let CM handle the down arrow and
113 // prevent the global keydown handler from handling it.
112 // prevent the global keydown handler from handling it.
114 if (!that.at_bottom()) {
113 if (!that.at_bottom()) {
115 event.stop();
114 event.stop();
116 return false;
115 return false;
117 } else {
116 } else {
118 return true;
117 return true;
119 };
118 };
120 } else if (event.keyCode === key.tab && event.type == 'keydown') {
119 } else if (event.keyCode === key.tab && event.type == 'keydown') {
121 // Tab completion.
120 // Tab completion.
122 var cur = editor.getCursor();
121 var cur = editor.getCursor();
123 //Do not trim here because of tooltip
122 //Do not trim here because of tooltip
124 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
123 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
125 if (pre_cursor.trim() === "") {
124 if (pre_cursor.trim() === "") {
126 // Don't autocomplete if the part of the line before the cursor
125 // Don't autocomplete if the part of the line before the cursor
127 // is empty. In this case, let CodeMirror handle indentation.
126 // is empty. In this case, let CodeMirror handle indentation.
128 return false;
127 return false;
129 } else if ((pre_cursor.substr(-1) === "("|| pre_cursor.substr(-1) === " ") && tooltip_on_tab ) {
128 } else if ((pre_cursor.substr(-1) === "("|| pre_cursor.substr(-1) === " ") && tooltip_on_tab ) {
130 that.request_tooltip_after_time(pre_cursor,0);
129 that.request_tooltip_after_time(pre_cursor,0);
131 // Prevent the event from bubbling up.
130 // Prevent the event from bubbling up.
132 event.stop();
131 event.stop();
133 // Prevent CodeMirror from handling the tab.
132 // Prevent CodeMirror from handling the tab.
134 return true;
133 return true;
135 } else {
134 } else {
136 event.stop();
135 event.stop();
137 this.completer.startCompletion();
136 this.completer.startCompletion();
138 return true;
137 return true;
139 };
138 };
140 } else if (event.keyCode === key.backspace && event.type == 'keydown') {
139 } else if (event.keyCode === key.backspace && event.type == 'keydown') {
141 // If backspace and the line ends with 4 spaces, remove them.
140 // If backspace and the line ends with 4 spaces, remove them.
142 var cur = editor.getCursor();
141 var cur = editor.getCursor();
143 var line = editor.getLine(cur.line);
142 var line = editor.getLine(cur.line);
144 var ending = line.slice(-4);
143 var ending = line.slice(-4);
145 if (ending === ' ') {
144 if (ending === ' ') {
146 editor.replaceRange('',
145 editor.replaceRange('',
147 {line: cur.line, ch: cur.ch-4},
146 {line: cur.line, ch: cur.ch-4},
148 {line: cur.line, ch: cur.ch}
147 {line: cur.line, ch: cur.ch}
149 );
148 );
150 event.stop();
149 event.stop();
151 return true;
150 return true;
152 } else {
151 } else {
153 return false;
152 return false;
154 };
153 };
155 } else {
154 } else {
156 // keypress/keyup also trigger on TAB press, and we don't want to
155 // keypress/keyup also trigger on TAB press, and we don't want to
157 // use those to disable tab completion.
156 // use those to disable tab completion.
158 return false;
157 return false;
159 };
158 };
160 return false;
159 return false;
161 };
160 };
162
161
163 CodeCell.prototype.remove_and_cancel_tooltip = function() {
162 CodeCell.prototype.remove_and_cancel_tooltip = function() {
164 // note that we don't handle closing directly inside the calltip
163 // note that we don't handle closing directly inside the calltip
165 // as in the completer, because it is not focusable, so won't
164 // as in the completer, because it is not focusable, so won't
166 // get the event.
165 // get the event.
167 if (this.tooltip_timeout != null){
166 if (this.tooltip_timeout != null){
168 clearTimeout(this.tooltip_timeout);
167 clearTimeout(this.tooltip_timeout);
169 $('#tooltip').remove();
168 $('#tooltip').remove();
170 this.tooltip_timeout = null;
169 this.tooltip_timeout = null;
171 }
170 }
172 }
171 }
173
172
174 CodeCell.prototype.finish_tooltip = function (reply) {
173 CodeCell.prototype.finish_tooltip = function (reply) {
175 // Extract call tip data; the priority is call, init, main.
174 // Extract call tip data; the priority is call, init, main.
176 defstring = reply.call_def;
175 defstring = reply.call_def;
177 if (defstring == null) { defstring = reply.init_definition; }
176 if (defstring == null) { defstring = reply.init_definition; }
178 if (defstring == null) { defstring = reply.definition; }
177 if (defstring == null) { defstring = reply.definition; }
179
178
180 docstring = reply.call_docstring;
179 docstring = reply.call_docstring;
181 if (docstring == null) { docstring = reply.init_docstring; }
180 if (docstring == null) { docstring = reply.init_docstring; }
182 if (docstring == null) { docstring = reply.docstring; }
181 if (docstring == null) { docstring = reply.docstring; }
183 if (docstring == null) { docstring = "<empty docstring>"; }
182 if (docstring == null) { docstring = "<empty docstring>"; }
184
183
185 name=reply.name;
184 name=reply.name;
186
185
187 var that = this;
186 var that = this;
188 var tooltip = $('<div/>').attr('id', 'tooltip').addClass('tooltip');
187 var tooltip = $('<div/>').attr('id', 'tooltip').addClass('tooltip');
189 // remove to have the tooltip not Limited in X and Y
188 // remove to have the tooltip not Limited in X and Y
190 tooltip.addClass('smalltooltip');
189 tooltip.addClass('smalltooltip');
191 var pre=$('<pre/>').html(utils.fixConsole(docstring));
190 var pre=$('<pre/>').html(utils.fixConsole(docstring));
192 var expandlink=$('<a/>').attr('href',"#");
191 var expandlink=$('<a/>').attr('href',"#");
193 expandlink.addClass("ui-corner-all"); //rounded corner
192 expandlink.addClass("ui-corner-all"); //rounded corner
194 expandlink.attr('role',"button");
193 expandlink.attr('role',"button");
195 //expandlink.addClass('ui-button');
194 //expandlink.addClass('ui-button');
196 //expandlink.addClass('ui-state-default');
195 //expandlink.addClass('ui-state-default');
197 var expandspan=$('<span/>').text('Expand');
196 var expandspan=$('<span/>').text('Expand');
198 expandspan.addClass('ui-icon');
197 expandspan.addClass('ui-icon');
199 expandspan.addClass('ui-icon-plus');
198 expandspan.addClass('ui-icon-plus');
200 expandlink.append(expandspan);
199 expandlink.append(expandspan);
201 expandlink.attr('id','expanbutton');
200 expandlink.attr('id','expanbutton');
202 expandlink.click(function(){
201 expandlink.click(function(){
203 tooltip.removeClass('smalltooltip');
202 tooltip.removeClass('smalltooltip');
204 tooltip.addClass('bigtooltip');
203 tooltip.addClass('bigtooltip');
205 $('#expanbutton').remove();
204 $('#expanbutton').remove();
206 setTimeout(function(){that.code_mirror.focus();}, 50);
205 setTimeout(function(){that.code_mirror.focus();}, 50);
207 });
206 });
208 var morelink=$('<a/>').attr('href',"#");
207 var morelink=$('<a/>').attr('href',"#");
209 morelink.attr('role',"button");
208 morelink.attr('role',"button");
210 morelink.addClass('ui-button');
209 morelink.addClass('ui-button');
211 //morelink.addClass("ui-corner-all"); //rounded corner
210 //morelink.addClass("ui-corner-all"); //rounded corner
212 //morelink.addClass('ui-state-default');
211 //morelink.addClass('ui-state-default');
213 var morespan=$('<span/>').text('Open in Pager');
212 var morespan=$('<span/>').text('Open in Pager');
214 morespan.addClass('ui-icon');
213 morespan.addClass('ui-icon');
215 morespan.addClass('ui-icon-arrowstop-l-n');
214 morespan.addClass('ui-icon-arrowstop-l-n');
216 morelink.append(morespan);
215 morelink.append(morespan);
217 morelink.click(function(){
216 morelink.click(function(){
218 var msg_id = IPython.notebook.kernel.execute(name+"?");
217 var msg_id = IPython.notebook.kernel.execute(name+"?");
219 IPython.notebook.msg_cell_map[msg_id] = IPython.notebook.get_selected_cell().cell_id;
218 IPython.notebook.msg_cell_map[msg_id] = IPython.notebook.get_selected_cell().cell_id;
220 that.remove_and_cancel_tooltip();
219 that.remove_and_cancel_tooltip();
221 setTimeout(function(){that.code_mirror.focus();}, 50);
220 setTimeout(function(){that.code_mirror.focus();}, 50);
222 });
221 });
223
222
224 var closelink=$('<a/>').attr('href',"#");
223 var closelink=$('<a/>').attr('href',"#");
225 closelink.attr('role',"button");
224 closelink.attr('role',"button");
226 closelink.addClass('ui-button');
225 closelink.addClass('ui-button');
227 //closelink.addClass("ui-corner-all"); //rounded corner
226 //closelink.addClass("ui-corner-all"); //rounded corner
228 //closelink.adClass('ui-state-default'); // grey background and blue cross
227 //closelink.adClass('ui-state-default'); // grey background and blue cross
229 var closespan=$('<span/>').text('Close');
228 var closespan=$('<span/>').text('Close');
230 closespan.addClass('ui-icon');
229 closespan.addClass('ui-icon');
231 closespan.addClass('ui-icon-close');
230 closespan.addClass('ui-icon-close');
232 closelink.append(closespan);
231 closelink.append(closespan);
233 closelink.click(function(){
232 closelink.click(function(){
234 that.remove_and_cancel_tooltip();
233 that.remove_and_cancel_tooltip();
235 setTimeout(function(){that.code_mirror.focus();}, 50);
234 setTimeout(function(){that.code_mirror.focus();}, 50);
236 });
235 });
237 //construct the tooltip
236 //construct the tooltip
238 tooltip.append(closelink);
237 tooltip.append(closelink);
239 tooltip.append(expandlink);
238 tooltip.append(expandlink);
240 tooltip.append(morelink);
239 tooltip.append(morelink);
241 if(defstring){
240 if(defstring){
242 defstring_html = $('<pre/>').html(utils.fixConsole(defstring));
241 defstring_html = $('<pre/>').html(utils.fixConsole(defstring));
243 tooltip.append(defstring_html);
242 tooltip.append(defstring_html);
244 }
243 }
245 tooltip.append(pre);
244 tooltip.append(pre);
246 var pos = this.code_mirror.cursorCoords();
245 var pos = this.code_mirror.cursorCoords();
247 tooltip.css('left',pos.x+'px');
246 tooltip.css('left',pos.x+'px');
248 tooltip.css('top',pos.yBot+'px');
247 tooltip.css('top',pos.yBot+'px');
249 $('body').append(tooltip);
248 $('body').append(tooltip);
250
249
251 // issues with cross-closing if multiple tooltip in less than 5sec
250 // issues with cross-closing if multiple tooltip in less than 5sec
252 // keep it comented for now
251 // keep it comented for now
253 // setTimeout(that.remove_and_cancel_tooltip, 5000);
252 // setTimeout(that.remove_and_cancel_tooltip, 5000);
254 };
253 };
255
254
256
255
257 // called when completion came back from the kernel. this will inspect the
256 // called when completion came back from the kernel. just forward
258 // curent cell for (more) completion merge the resuults with the ones
259 // comming from the kernel and forward it to the completer
260 CodeCell.prototype.finish_completing = function (matched_text, matches) {
257 CodeCell.prototype.finish_completing = function (matched_text, matches) {
261 this.completer.finish_completing(matched_text,matches);
258 this.completer.finish_completing(matched_text,matches);
262 }
259 }
263
260
264
261
265 CodeCell.prototype.select = function () {
262 CodeCell.prototype.select = function () {
266 IPython.Cell.prototype.select.apply(this);
263 IPython.Cell.prototype.select.apply(this);
267 this.code_mirror.refresh();
264 this.code_mirror.refresh();
268 this.code_mirror.focus();
265 this.code_mirror.focus();
269 // We used to need an additional refresh() after the focus, but
266 // We used to need an additional refresh() after the focus, but
270 // it appears that this has been fixed in CM. This bug would show
267 // it appears that this has been fixed in CM. This bug would show
271 // up on FF when a newly loaded markdown cell was edited.
268 // up on FF when a newly loaded markdown cell was edited.
272 };
269 };
273
270
274
271
275 CodeCell.prototype.select_all = function () {
272 CodeCell.prototype.select_all = function () {
276 var start = {line: 0, ch: 0};
273 var start = {line: 0, ch: 0};
277 var nlines = this.code_mirror.lineCount();
274 var nlines = this.code_mirror.lineCount();
278 var last_line = this.code_mirror.getLine(nlines-1);
275 var last_line = this.code_mirror.getLine(nlines-1);
279 var end = {line: nlines-1, ch: last_line.length};
276 var end = {line: nlines-1, ch: last_line.length};
280 this.code_mirror.setSelection(start, end);
277 this.code_mirror.setSelection(start, end);
281 };
278 };
282
279
283
280
284 CodeCell.prototype.append_output = function (json, dynamic) {
281 CodeCell.prototype.append_output = function (json, dynamic) {
285 // If dynamic is true, javascript output will be eval'd.
282 // If dynamic is true, javascript output will be eval'd.
286 this.expand();
283 this.expand();
287 this.flush_clear_timeout();
284 this.flush_clear_timeout();
288 if (json.output_type === 'pyout') {
285 if (json.output_type === 'pyout') {
289 this.append_pyout(json, dynamic);
286 this.append_pyout(json, dynamic);
290 } else if (json.output_type === 'pyerr') {
287 } else if (json.output_type === 'pyerr') {
291 this.append_pyerr(json);
288 this.append_pyerr(json);
292 } else if (json.output_type === 'display_data') {
289 } else if (json.output_type === 'display_data') {
293 this.append_display_data(json, dynamic);
290 this.append_display_data(json, dynamic);
294 } else if (json.output_type === 'stream') {
291 } else if (json.output_type === 'stream') {
295 this.append_stream(json);
292 this.append_stream(json);
296 };
293 };
297 this.outputs.push(json);
294 this.outputs.push(json);
298 };
295 };
299
296
300
297
301 CodeCell.prototype.create_output_area = function () {
298 CodeCell.prototype.create_output_area = function () {
302 var oa = $("<div/>").addClass("hbox output_area");
299 var oa = $("<div/>").addClass("hbox output_area");
303 oa.append($('<div/>').addClass('prompt'));
300 oa.append($('<div/>').addClass('prompt'));
304 return oa;
301 return oa;
305 };
302 };
306
303
307
304
308 CodeCell.prototype.append_pyout = function (json, dynamic) {
305 CodeCell.prototype.append_pyout = function (json, dynamic) {
309 var n = json.prompt_number || ' ';
306 var n = json.prompt_number || ' ';
310 var toinsert = this.create_output_area();
307 var toinsert = this.create_output_area();
311 toinsert.find('div.prompt').addClass('output_prompt').html('Out[' + n + ']:');
308 toinsert.find('div.prompt').addClass('output_prompt').html('Out[' + n + ']:');
312 this.append_mime_type(json, toinsert, dynamic);
309 this.append_mime_type(json, toinsert, dynamic);
313 this.element.find('div.output').append(toinsert);
310 this.element.find('div.output').append(toinsert);
314 // If we just output latex, typeset it.
311 // If we just output latex, typeset it.
315 if ((json.latex !== undefined) || (json.html !== undefined)) {
312 if ((json.latex !== undefined) || (json.html !== undefined)) {
316 this.typeset();
313 this.typeset();
317 };
314 };
318 };
315 };
319
316
320
317
321 CodeCell.prototype.append_pyerr = function (json) {
318 CodeCell.prototype.append_pyerr = function (json) {
322 var tb = json.traceback;
319 var tb = json.traceback;
323 if (tb !== undefined && tb.length > 0) {
320 if (tb !== undefined && tb.length > 0) {
324 var s = '';
321 var s = '';
325 var len = tb.length;
322 var len = tb.length;
326 for (var i=0; i<len; i++) {
323 for (var i=0; i<len; i++) {
327 s = s + tb[i] + '\n';
324 s = s + tb[i] + '\n';
328 }
325 }
329 s = s + '\n';
326 s = s + '\n';
330 var toinsert = this.create_output_area();
327 var toinsert = this.create_output_area();
331 this.append_text(s, toinsert);
328 this.append_text(s, toinsert);
332 this.element.find('div.output').append(toinsert);
329 this.element.find('div.output').append(toinsert);
333 };
330 };
334 };
331 };
335
332
336
333
337 CodeCell.prototype.append_stream = function (json) {
334 CodeCell.prototype.append_stream = function (json) {
338 // temporary fix: if stream undefined (json file written prior to this patch),
335 // temporary fix: if stream undefined (json file written prior to this patch),
339 // default to most likely stdout:
336 // default to most likely stdout:
340 if (json.stream == undefined){
337 if (json.stream == undefined){
341 json.stream = 'stdout';
338 json.stream = 'stdout';
342 }
339 }
343 if (!utils.fixConsole(json.text)){
340 if (!utils.fixConsole(json.text)){
344 // fixConsole gives nothing (empty string, \r, etc.)
341 // fixConsole gives nothing (empty string, \r, etc.)
345 // so don't append any elements, which might add undesirable space
342 // so don't append any elements, which might add undesirable space
346 return;
343 return;
347 }
344 }
348 var subclass = "output_"+json.stream;
345 var subclass = "output_"+json.stream;
349 if (this.outputs.length > 0){
346 if (this.outputs.length > 0){
350 // have at least one output to consider
347 // have at least one output to consider
351 var last = this.outputs[this.outputs.length-1];
348 var last = this.outputs[this.outputs.length-1];
352 if (last.output_type == 'stream' && json.stream == last.stream){
349 if (last.output_type == 'stream' && json.stream == last.stream){
353 // latest output was in the same stream,
350 // latest output was in the same stream,
354 // so append directly into its pre tag
351 // so append directly into its pre tag
355 // escape ANSI & HTML specials:
352 // escape ANSI & HTML specials:
356 var text = utils.fixConsole(json.text);
353 var text = utils.fixConsole(json.text);
357 this.element.find('div.'+subclass).last().find('pre').append(text);
354 this.element.find('div.'+subclass).last().find('pre').append(text);
358 return;
355 return;
359 }
356 }
360 }
357 }
361
358
362 // If we got here, attach a new div
359 // If we got here, attach a new div
363 var toinsert = this.create_output_area();
360 var toinsert = this.create_output_area();
364 this.append_text(json.text, toinsert, "output_stream "+subclass);
361 this.append_text(json.text, toinsert, "output_stream "+subclass);
365 this.element.find('div.output').append(toinsert);
362 this.element.find('div.output').append(toinsert);
366 };
363 };
367
364
368
365
369 CodeCell.prototype.append_display_data = function (json, dynamic) {
366 CodeCell.prototype.append_display_data = function (json, dynamic) {
370 var toinsert = this.create_output_area();
367 var toinsert = this.create_output_area();
371 this.append_mime_type(json, toinsert, dynamic);
368 this.append_mime_type(json, toinsert, dynamic);
372 this.element.find('div.output').append(toinsert);
369 this.element.find('div.output').append(toinsert);
373 // If we just output latex, typeset it.
370 // If we just output latex, typeset it.
374 if ( (json.latex !== undefined) || (json.html !== undefined) ) {
371 if ( (json.latex !== undefined) || (json.html !== undefined) ) {
375 this.typeset();
372 this.typeset();
376 };
373 };
377 };
374 };
378
375
379
376
380 CodeCell.prototype.append_mime_type = function (json, element, dynamic) {
377 CodeCell.prototype.append_mime_type = function (json, element, dynamic) {
381 if (json.javascript !== undefined && dynamic) {
378 if (json.javascript !== undefined && dynamic) {
382 this.append_javascript(json.javascript, element, dynamic);
379 this.append_javascript(json.javascript, element, dynamic);
383 } else if (json.html !== undefined) {
380 } else if (json.html !== undefined) {
384 this.append_html(json.html, element);
381 this.append_html(json.html, element);
385 } else if (json.latex !== undefined) {
382 } else if (json.latex !== undefined) {
386 this.append_latex(json.latex, element);
383 this.append_latex(json.latex, element);
387 } else if (json.svg !== undefined) {
384 } else if (json.svg !== undefined) {
388 this.append_svg(json.svg, element);
385 this.append_svg(json.svg, element);
389 } else if (json.png !== undefined) {
386 } else if (json.png !== undefined) {
390 this.append_png(json.png, element);
387 this.append_png(json.png, element);
391 } else if (json.jpeg !== undefined) {
388 } else if (json.jpeg !== undefined) {
392 this.append_jpeg(json.jpeg, element);
389 this.append_jpeg(json.jpeg, element);
393 } else if (json.text !== undefined) {
390 } else if (json.text !== undefined) {
394 this.append_text(json.text, element);
391 this.append_text(json.text, element);
395 };
392 };
396 };
393 };
397
394
398
395
399 CodeCell.prototype.append_html = function (html, element) {
396 CodeCell.prototype.append_html = function (html, element) {
400 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_html rendered_html");
397 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_html rendered_html");
401 toinsert.append(html);
398 toinsert.append(html);
402 element.append(toinsert);
399 element.append(toinsert);
403 };
400 };
404
401
405
402
406 CodeCell.prototype.append_javascript = function (js, container) {
403 CodeCell.prototype.append_javascript = function (js, container) {
407 // We just eval the JS code, element appears in the local scope.
404 // We just eval the JS code, element appears in the local scope.
408 var element = $("<div/>").addClass("box_flex1 output_subarea");
405 var element = $("<div/>").addClass("box_flex1 output_subarea");
409 container.append(element);
406 container.append(element);
410 // Div for js shouldn't be drawn, as it will add empty height to the area.
407 // Div for js shouldn't be drawn, as it will add empty height to the area.
411 container.hide();
408 container.hide();
412 // If the Javascript appends content to `element` that should be drawn, then
409 // If the Javascript appends content to `element` that should be drawn, then
413 // it must also call `container.show()`.
410 // it must also call `container.show()`.
414 eval(js);
411 eval(js);
415 }
412 }
416
413
417
414
418 CodeCell.prototype.append_text = function (data, element, extra_class) {
415 CodeCell.prototype.append_text = function (data, element, extra_class) {
419 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_text");
416 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_text");
420 // escape ANSI & HTML specials in plaintext:
417 // escape ANSI & HTML specials in plaintext:
421 data = utils.fixConsole(data);
418 data = utils.fixConsole(data);
422 if (extra_class){
419 if (extra_class){
423 toinsert.addClass(extra_class);
420 toinsert.addClass(extra_class);
424 }
421 }
425 toinsert.append($("<pre/>").html(data));
422 toinsert.append($("<pre/>").html(data));
426 element.append(toinsert);
423 element.append(toinsert);
427 };
424 };
428
425
429
426
430 CodeCell.prototype.append_svg = function (svg, element) {
427 CodeCell.prototype.append_svg = function (svg, element) {
431 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_svg");
428 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_svg");
432 toinsert.append(svg);
429 toinsert.append(svg);
433 element.append(toinsert);
430 element.append(toinsert);
434 };
431 };
435
432
436
433
437 CodeCell.prototype.append_png = function (png, element) {
434 CodeCell.prototype.append_png = function (png, element) {
438 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_png");
435 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_png");
439 toinsert.append($("<img/>").attr('src','data:image/png;base64,'+png));
436 toinsert.append($("<img/>").attr('src','data:image/png;base64,'+png));
440 element.append(toinsert);
437 element.append(toinsert);
441 };
438 };
442
439
443
440
444 CodeCell.prototype.append_jpeg = function (jpeg, element) {
441 CodeCell.prototype.append_jpeg = function (jpeg, element) {
445 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_jpeg");
442 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_jpeg");
446 toinsert.append($("<img/>").attr('src','data:image/jpeg;base64,'+jpeg));
443 toinsert.append($("<img/>").attr('src','data:image/jpeg;base64,'+jpeg));
447 element.append(toinsert);
444 element.append(toinsert);
448 };
445 };
449
446
450
447
451 CodeCell.prototype.append_latex = function (latex, element) {
448 CodeCell.prototype.append_latex = function (latex, element) {
452 // This method cannot do the typesetting because the latex first has to
449 // This method cannot do the typesetting because the latex first has to
453 // be on the page.
450 // be on the page.
454 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_latex");
451 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_latex");
455 toinsert.append(latex);
452 toinsert.append(latex);
456 element.append(toinsert);
453 element.append(toinsert);
457 };
454 };
458
455
459
456
460 CodeCell.prototype.clear_output = function (stdout, stderr, other) {
457 CodeCell.prototype.clear_output = function (stdout, stderr, other) {
461 var that = this;
458 var that = this;
462 if (this.clear_out_timeout != null){
459 if (this.clear_out_timeout != null){
463 // fire previous pending clear *immediately*
460 // fire previous pending clear *immediately*
464 clearTimeout(this.clear_out_timeout);
461 clearTimeout(this.clear_out_timeout);
465 this.clear_out_timeout = null;
462 this.clear_out_timeout = null;
466 this.clear_output_callback(this._clear_stdout, this._clear_stderr, this._clear_other);
463 this.clear_output_callback(this._clear_stdout, this._clear_stderr, this._clear_other);
467 }
464 }
468 // store flags for flushing the timeout
465 // store flags for flushing the timeout
469 this._clear_stdout = stdout;
466 this._clear_stdout = stdout;
470 this._clear_stderr = stderr;
467 this._clear_stderr = stderr;
471 this._clear_other = other;
468 this._clear_other = other;
472 this.clear_out_timeout = setTimeout(function(){
469 this.clear_out_timeout = setTimeout(function(){
473 // really clear timeout only after a short delay
470 // really clear timeout only after a short delay
474 // this reduces flicker in 'clear_output; print' cases
471 // this reduces flicker in 'clear_output; print' cases
475 that.clear_out_timeout = null;
472 that.clear_out_timeout = null;
476 that._clear_stdout = that._clear_stderr = that._clear_other = null;
473 that._clear_stdout = that._clear_stderr = that._clear_other = null;
477 that.clear_output_callback(stdout, stderr, other);
474 that.clear_output_callback(stdout, stderr, other);
478 }, 500
475 }, 500
479 );
476 );
480 };
477 };
481
478
482 CodeCell.prototype.clear_output_callback = function (stdout, stderr, other) {
479 CodeCell.prototype.clear_output_callback = function (stdout, stderr, other) {
483 var output_div = this.element.find("div.output");
480 var output_div = this.element.find("div.output");
484
481
485 if (stdout && stderr && other){
482 if (stdout && stderr && other){
486 // clear all, no need for logic
483 // clear all, no need for logic
487 output_div.html("");
484 output_div.html("");
488 this.outputs = [];
485 this.outputs = [];
489 return;
486 return;
490 }
487 }
491 // remove html output
488 // remove html output
492 // each output_subarea that has an identifying class is in an output_area
489 // each output_subarea that has an identifying class is in an output_area
493 // which is the element to be removed.
490 // which is the element to be removed.
494 if (stdout){
491 if (stdout){
495 output_div.find("div.output_stdout").parent().remove();
492 output_div.find("div.output_stdout").parent().remove();
496 }
493 }
497 if (stderr){
494 if (stderr){
498 output_div.find("div.output_stderr").parent().remove();
495 output_div.find("div.output_stderr").parent().remove();
499 }
496 }
500 if (other){
497 if (other){
501 output_div.find("div.output_subarea").not("div.output_stderr").not("div.output_stdout").parent().remove();
498 output_div.find("div.output_subarea").not("div.output_stderr").not("div.output_stdout").parent().remove();
502 }
499 }
503
500
504 // remove cleared outputs from JSON list:
501 // remove cleared outputs from JSON list:
505 for (var i = this.outputs.length - 1; i >= 0; i--){
502 for (var i = this.outputs.length - 1; i >= 0; i--){
506 var out = this.outputs[i];
503 var out = this.outputs[i];
507 var output_type = out.output_type;
504 var output_type = out.output_type;
508 if (output_type == "display_data" && other){
505 if (output_type == "display_data" && other){
509 this.outputs.splice(i,1);
506 this.outputs.splice(i,1);
510 }else if (output_type == "stream"){
507 }else if (output_type == "stream"){
511 if (stdout && out.stream == "stdout"){
508 if (stdout && out.stream == "stdout"){
512 this.outputs.splice(i,1);
509 this.outputs.splice(i,1);
513 }else if (stderr && out.stream == "stderr"){
510 }else if (stderr && out.stream == "stderr"){
514 this.outputs.splice(i,1);
511 this.outputs.splice(i,1);
515 }
512 }
516 }
513 }
517 }
514 }
518 };
515 };
519
516
520
517
521 CodeCell.prototype.clear_input = function () {
518 CodeCell.prototype.clear_input = function () {
522 this.code_mirror.setValue('');
519 this.code_mirror.setValue('');
523 };
520 };
524
521
525 CodeCell.prototype.flush_clear_timeout = function() {
522 CodeCell.prototype.flush_clear_timeout = function() {
526 var output_div = this.element.find('div.output');
523 var output_div = this.element.find('div.output');
527 if (this.clear_out_timeout){
524 if (this.clear_out_timeout){
528 clearTimeout(this.clear_out_timeout);
525 clearTimeout(this.clear_out_timeout);
529 this.clear_out_timeout = null;
526 this.clear_out_timeout = null;
530 this.clear_output_callback(this._clear_stdout, this._clear_stderr, this._clear_other);
527 this.clear_output_callback(this._clear_stdout, this._clear_stderr, this._clear_other);
531 };
528 };
532 }
529 }
533
530
534
531
535 CodeCell.prototype.collapse = function () {
532 CodeCell.prototype.collapse = function () {
536 if (!this.collapsed) {
533 if (!this.collapsed) {
537 this.element.find('div.output').hide();
534 this.element.find('div.output').hide();
538 this.collapsed = true;
535 this.collapsed = true;
539 };
536 };
540 };
537 };
541
538
542
539
543 CodeCell.prototype.expand = function () {
540 CodeCell.prototype.expand = function () {
544 if (this.collapsed) {
541 if (this.collapsed) {
545 this.element.find('div.output').show();
542 this.element.find('div.output').show();
546 this.collapsed = false;
543 this.collapsed = false;
547 };
544 };
548 };
545 };
549
546
550
547
551 CodeCell.prototype.toggle_output = function () {
548 CodeCell.prototype.toggle_output = function () {
552 if (this.collapsed) {
549 if (this.collapsed) {
553 this.expand();
550 this.expand();
554 } else {
551 } else {
555 this.collapse();
552 this.collapse();
556 };
553 };
557 };
554 };
558
555
559 CodeCell.prototype.set_input_prompt = function (number) {
556 CodeCell.prototype.set_input_prompt = function (number) {
560 this.input_prompt_number = number;
557 this.input_prompt_number = number;
561 var ns = number || "&nbsp;";
558 var ns = number || "&nbsp;";
562 this.element.find('div.input_prompt').html('In&nbsp;[' + ns + ']:');
559 this.element.find('div.input_prompt').html('In&nbsp;[' + ns + ']:');
563 };
560 };
564
561
565
562
566 CodeCell.prototype.get_text = function () {
563 CodeCell.prototype.get_text = function () {
567 return this.code_mirror.getValue();
564 return this.code_mirror.getValue();
568 };
565 };
569
566
570
567
571 CodeCell.prototype.set_text = function (code) {
568 CodeCell.prototype.set_text = function (code) {
572 return this.code_mirror.setValue(code);
569 return this.code_mirror.setValue(code);
573 };
570 };
574
571
575
572
576 CodeCell.prototype.at_top = function () {
573 CodeCell.prototype.at_top = function () {
577 var cursor = this.code_mirror.getCursor();
574 var cursor = this.code_mirror.getCursor();
578 if (cursor.line === 0) {
575 if (cursor.line === 0) {
579 return true;
576 return true;
580 } else {
577 } else {
581 return false;
578 return false;
582 }
579 }
583 };
580 };
584
581
585
582
586 CodeCell.prototype.at_bottom = function () {
583 CodeCell.prototype.at_bottom = function () {
587 var cursor = this.code_mirror.getCursor();
584 var cursor = this.code_mirror.getCursor();
588 if (cursor.line === (this.code_mirror.lineCount()-1)) {
585 if (cursor.line === (this.code_mirror.lineCount()-1)) {
589 return true;
586 return true;
590 } else {
587 } else {
591 return false;
588 return false;
592 }
589 }
593 };
590 };
594
591
595
592
596 CodeCell.prototype.fromJSON = function (data) {
593 CodeCell.prototype.fromJSON = function (data) {
597 if (data.cell_type === 'code') {
594 if (data.cell_type === 'code') {
598 if (data.input !== undefined) {
595 if (data.input !== undefined) {
599 this.set_text(data.input);
596 this.set_text(data.input);
600 }
597 }
601 if (data.prompt_number !== undefined) {
598 if (data.prompt_number !== undefined) {
602 this.set_input_prompt(data.prompt_number);
599 this.set_input_prompt(data.prompt_number);
603 } else {
600 } else {
604 this.set_input_prompt();
601 this.set_input_prompt();
605 };
602 };
606 var len = data.outputs.length;
603 var len = data.outputs.length;
607 for (var i=0; i<len; i++) {
604 for (var i=0; i<len; i++) {
608 // append with dynamic=false.
605 // append with dynamic=false.
609 this.append_output(data.outputs[i], false);
606 this.append_output(data.outputs[i], false);
610 };
607 };
611 if (data.collapsed !== undefined) {
608 if (data.collapsed !== undefined) {
612 if (data.collapsed) {
609 if (data.collapsed) {
613 this.collapse();
610 this.collapse();
614 } else {
611 } else {
615 this.expand();
612 this.expand();
616 };
613 };
617 };
614 };
618 };
615 };
619 };
616 };
620
617
621
618
622 CodeCell.prototype.toJSON = function () {
619 CodeCell.prototype.toJSON = function () {
623 var data = {};
620 var data = {};
624 data.input = this.get_text();
621 data.input = this.get_text();
625 data.cell_type = 'code';
622 data.cell_type = 'code';
626 if (this.input_prompt_number) {
623 if (this.input_prompt_number) {
627 data.prompt_number = this.input_prompt_number;
624 data.prompt_number = this.input_prompt_number;
628 };
625 };
629 var outputs = [];
626 var outputs = [];
630 var len = this.outputs.length;
627 var len = this.outputs.length;
631 for (var i=0; i<len; i++) {
628 for (var i=0; i<len; i++) {
632 outputs[i] = this.outputs[i];
629 outputs[i] = this.outputs[i];
633 };
630 };
634 data.outputs = outputs;
631 data.outputs = outputs;
635 data.language = 'python';
632 data.language = 'python';
636 data.collapsed = this.collapsed;
633 data.collapsed = this.collapsed;
637 return data;
634 return data;
638 };
635 };
639
636
640
637
641 IPython.CodeCell = CodeCell;
638 IPython.CodeCell = CodeCell;
642
639
643 return IPython;
640 return IPython;
644 }(IPython));
641 }(IPython));
@@ -1,246 +1,230
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 //
5
4
6 var IPython = (function(IPython ) {
5 var IPython = (function(IPython ) {
7 // that will prevent us from misspelling
6 // that will prevent us from misspelling
8 "use strict";
7 "use strict";
9
8
10 // easyier key mapping
9 // easyier key mapping
11 var key = IPython.utils.keycodes;
10 var key = IPython.utils.keycodes;
12
11
13 // what is the common start of all completions
12 // what is the common start of all completions
14 function sharedStart(B){
13 function sharedStart(B){
15 if(B.length == 1){return B[0]}
14 if(B.length == 1){return B[0]}
16 var A = new Array()
15 var A = new Array()
17 for(var i=0; i< B.length; i++)
16 for(var i=0; i< B.length; i++)
18 {
17 {
19 A.push(B[i].str);
18 A.push(B[i].str);
20 }
19 }
21 if(A.length > 1 ){
20 if(A.length > 1 ){
22 var tem1, tem2, s, A = A.slice(0).sort();
21 var tem1, tem2, s, A = A.slice(0).sort();
23 tem1 = A[0];
22 tem1 = A[0];
24 s = tem1.length;
23 s = tem1.length;
25 tem2 = A.pop();
24 tem2 = A.pop();
26 while(s && tem2.indexOf(tem1) == -1){
25 while(s && tem2.indexOf(tem1) == -1){
27 tem1 = tem1.substring(0, --s);
26 tem1 = tem1.substring(0, --s);
28 }
27 }
29 if (tem1 == "" || tem2.indexOf(tem1) != 0){return null;}
28 if (tem1 == "" || tem2.indexOf(tem1) != 0){return null;}
30 return { str : tem1,
29 return { str : tem1,
31 type : "computed",
30 type : "computed",
32 from : B[0].from,
31 from : B[0].from,
33 to : B[0].to
32 to : B[0].to
34 };
33 };
35 }
34 }
36 return null;
35 return null;
37 }
36 }
38
37
39
38
40 var Completer = function(cell) {
39 var Completer = function(cell) {
41 this.cell = cell;
40 this.cell = cell;
42 this.editor = cell.code_mirror;
41 this.editor = cell.code_mirror;
43 // 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
44 this.reg = /[A-Za-z.]/;
43 this.reg = /[A-Za-z.]/;
45 }
44 }
46
45
47 Completer.prototype.kernelCompletionRequest = function(){
46 Completer.prototype.kernelCompletionRequest = function(){
48 var cur = this.editor.getCursor();
47 var cur = this.editor.getCursor();
49 // Autocomplete the current line.
50 var line = this.editor.getLine(cur.line);
48 var line = this.editor.getLine(cur.line);
51 // one could fork here and directly call finish completing
49 // one could fork here and directly call finish completing if kernel is busy
52 // if kernel is busy
53 IPython.notebook.complete_cell(this.cell, line, cur.ch);
50 IPython.notebook.complete_cell(this.cell, line, cur.ch);
54 }
51 }
55
52
56
53
57 Completer.prototype.startCompletion = function()
54 Completer.prototype.startCompletion = function()
58 {
55 {
59 // 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
60 // special behaviour like autopicking if only one completion availlable
57 // special behaviour like autopicking if only one completion availlable
61 //
58 //
62 if (this.editor.somethingSelected()) return;
59 if (this.editor.somethingSelected()) return;
63 this.done = false;
60 this.done = false;
64 // use to get focus back on opera
61 // use to get focus back on opera
65 this.carryOnCompletion(true);
62 this.carryOnCompletion(true);
66 }
63 }
67
64
68 Completer.prototype.carryOnCompletion = function(ff)
65 Completer.prototype.carryOnCompletion = function(ff) {
69 {
66 // Pass true as parameter if you want the commpleter to autopick when
70 // pass true as parameter if you want the commpleter to autopick
67 // only one completion. This function is automatically reinvoked at
71 // when only one completion
68 // each keystroke with ff = false
72 // as this function is automatically reinvoked at each keystroke with
69
73 // ff = false
74 var cur = this.editor.getCursor();
70 var cur = this.editor.getCursor();
75 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);
76
72
77 // 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
78 // because while typing the completer is still reinvoking itself
74 // because while typing the completer is still reinvoking itself
79 if(!this.reg.test(pre_cursor)){ this.close(); return;}
75 if(!this.reg.test(pre_cursor)){ this.close(); return;}
80
76
81 this.autopick = false;
77 this.autopick = false;
82 if( ff != 'undefined' && ff==true)
78 if( ff != 'undefined' && ff==true)
83 {
79 { this.autopick=true; }
84 this.autopick=true;
80
85 }
86 // We want a single cursor position.
81 // We want a single cursor position.
87 if (this.editor.somethingSelected()) return;
82 if (this.editor.somethingSelected()) return;
88
83
89 //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
90 this.kernelCompletionRequest();
85 this.kernelCompletionRequest();
91 }
86 }
92
87
93 Completer.prototype.finish_completing =function (matched_text, matches) {
88 Completer.prototype.finish_completing =function (matched_text, matches) {
94 // let's build a function that wrap all that stuff into what is needed for the
89 // let's build a function that wrap all that stuff into what is needed
95 // new completer:
90 // for the new completer:
96 //
91
97 var cur = this.editor.getCursor();
92 var cur = this.editor.getCursor();
98 var results = CodeMirror.contextHint(this.editor);
93 var results = CodeMirror.contextHint(this.editor);
99
94
100 // append the introspection result, in order, at
95 // append the introspection result, in order, at at the beginning of
101 // at the beginning of the table and compute the replacement rance
96 // the table and compute the replacement range from current cursor
102 // from current cursor positon and matched_text length.
97 // positon and matched_text length.
103 for(var i= matches.length-1; i>=0 ;--i)
98 for(var i= matches.length-1; i>=0 ;--i)
104 {
99 {
105 results.unshift(
100 results.unshift(
106 {
101 { str : matches[i],
107 str : matches[i],
108 type : "introspection",
102 type : "introspection",
109 from : {line: cur.line, ch: cur.ch-matched_text.length},
103 from : {line: cur.line, ch: cur.ch-matched_text.length},
110 to : {line: cur.line, ch: cur.ch}
104 to : {line: cur.line, ch: cur.ch}
111 }
105 })
112 )
113 }
106 }
114
107
115 // one the 2 sources results have been merge, deal with it
108 // one the 2 sources results have been merge, deal with it
116 this.raw_result = results;
109 this.raw_result = results;
117
110
118 // if empty result return
111 // if empty result return
119 if (!this.raw_result || !this.raw_result.length) return;
112 if (!this.raw_result || !this.raw_result.length) return;
120
113
121 // When there is only one completion, use it directly.
114 // When there is only one completion, use it directly.
122 if (this.autopick == true && this.raw_result.length == 1)
115 if (this.autopick == true && this.raw_result.length == 1)
123 {
116 {
124 this.insert(this.raw_result[0]);
117 this.insert(this.raw_result[0]);
125 return true;
118 return true;
126 }
119 }
127
120
128 if (this.raw_result.length == 1)
121 if (this.raw_result.length == 1)
129 {
122 {
130 // test if first and only completion totally matches
123 // test if first and only completion totally matches
131 // what is typed, in this case dismiss
124 // what is typed, in this case dismiss
132 var str = this.raw_result[0].str
125 var str = this.raw_result[0].str
133 var cur = this.editor.getCursor();
126 var cur = this.editor.getCursor();
134 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);
135 if(pre_cursor == str){
128 if(pre_cursor == str)
136 this.close();
129 { this.close(); return ; }
137 return ;
138 }
139 }
130 }
140
131
141 this.complete = $('<div/>').addClass('completions');
132 this.complete = $('<div/>').addClass('completions');
142 this.complete.attr('id','complete');
133 this.complete.attr('id','complete');
143
134
144 this.sel = $('<select/>')
135 this.sel = $('<select/>')
145 .attr('multiple','true')
136 .attr('multiple','true')
146 .attr('size',Math.min(10,this.raw_result.length));
137 .attr('size',Math.min(10,this.raw_result.length));
147 var pos = this.editor.cursorCoords();
138 var pos = this.editor.cursorCoords();
148
139
149 // TODO: I propose to remove enough horizontal pixel
140 // TODO: I propose to remove enough horizontal pixel
150 // to align the text later
141 // to align the text later
151 this.complete.css('left',pos.x+'px');
142 this.complete.css('left',pos.x+'px');
152 this.complete.css('top',pos.yBot+'px');
143 this.complete.css('top',pos.yBot+'px');
153 this.complete.append(this.sel);
144 this.complete.append(this.sel);
154
145
155 $('body').append(this.complete);
146 $('body').append(this.complete);
156 //build the container
147 //build the container
157 var that = this;
148 var that = this;
158 this.sel.dblclick(function(){that.pick()});
149 this.sel.dblclick(function(){that.pick()});
159 this.sel.blur(this.close);
150 this.sel.blur(this.close);
160 this.sel.keydown(function(event){that.keydown(event)});
151 this.sel.keydown(function(event){that.keydown(event)});
161
152
162 this.build_gui_list(this.raw_result);
153 this.build_gui_list(this.raw_result);
163 //CodeMirror.connect(that.sel, "dblclick", function(){that.pick()});
164
154
165 this.sel.focus();
155 this.sel.focus();
166 // Opera sometimes ignores focusing a freshly created node
156 // Opera sometimes ignores focusing a freshly created node
167 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);
168 // why do we return true ?
169 return true;
158 return true;
170 }
159 }
171
160
172 Completer.prototype.insert = function(completion) {
161 Completer.prototype.insert = function(completion) {
173 this.editor.replaceRange(completion.str, completion.from, completion.to);
162 this.editor.replaceRange(completion.str, completion.from, completion.to);
174 }
163 }
175
164
176 Completer.prototype.build_gui_list = function(completions){
165 Completer.prototype.build_gui_list = function(completions){
177 // Need to clear the all list
166 // Need to clear the all list
178 for (var i = 0; i < completions.length; ++i) {
167 for (var i = 0; i < completions.length; ++i) {
179 var opt = $('<option/>')
168 var opt = $('<option/>')
180 .text(completions[i].str)
169 .text(completions[i].str)
181 .addClass(completions[i].type);
170 .addClass(completions[i].type);
182 this.sel.append(opt);
171 this.sel.append(opt);
183 }
172 }
184 this.sel.children().first().attr('selected','true');
173 this.sel.children().first().attr('selected','true');
185
186 //sel.size = Math.min(10, completions.length);
187 // Hack to hide the scrollbar.
188 //if (completions.length <= 10)
189 //this.complete.style.width = (this.sel.clientWidth - 1) + "px";
190 }
174 }
191
175
192 Completer.prototype.close = function() {
176 Completer.prototype.close = function() {
193 if (this.done) return;
177 if (this.done) return;
194 this.done = true;
178 this.done = true;
195 $('.completions').remove();
179 $('.completions').remove();
196 }
180 }
197
181
198 Completer.prototype.pick = function(){
182 Completer.prototype.pick = function(){
199 this.insert(this.raw_result[this.sel[0].selectedIndex]);
183 this.insert(this.raw_result[this.sel[0].selectedIndex]);
200 this.close();
184 this.close();
201 var that = this;
185 var that = this;
202 setTimeout(function(){that.editor.focus();}, 50);
186 setTimeout(function(){that.editor.focus();}, 50);
203 }
187 }
204
188
205
189
206 Completer.prototype.keydown = function(event) {
190 Completer.prototype.keydown = function(event) {
207 var code = event.keyCode;
191 var code = event.keyCode;
208 // Enter
192 // Enter
209 if (code == key.enter) {CodeMirror.e_stop(event); this.pick();}
193 if (code == key.enter) {CodeMirror.e_stop(event); this.pick();}
210 // Escape or backspace
194 // Escape or backspace
211 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();}
212 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();}
213 else if (code == key.tab){
197 else if (code == key.tab){
214 //all the fastforwarding operation,
198 //all the fastforwarding operation,
215 //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
216 // 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>
217 // to erase py
201 // to erase py
218 var sh = sharedStart(this.raw_result);
202 var sh = sharedStart(this.raw_result);
219 if(sh){
203 if(sh){
220 this.insert(sh);
204 this.insert(sh);
221 }
205 }
222 this.close();
206 this.close();
223 CodeMirror.e_stop(event);
207 CodeMirror.e_stop(event);
224 this.editor.focus();
208 this.editor.focus();
225 //reinvoke self
209 //reinvoke self
226 var that = this;
210 var that = this;
227 setTimeout(function(){that.carryOnCompletion();}, 50);
211 setTimeout(function(){that.carryOnCompletion();}, 50);
228 }
212 }
229 else if (code == key.upArrow || code == key.downArrow) {
213 else if (code == key.upArrow || code == key.downArrow) {
230 // need to do that to be able to move the arrow
214 // need to do that to be able to move the arrow
231 // when on the first or last line ofo a code cell
215 // when on the first or last line ofo a code cell
232 event.stopPropagation();
216 event.stopPropagation();
233 }
217 }
234 else if (code != key.upArrow && code != key.downArrow) {
218 else if (code != key.upArrow && code != key.downArrow) {
235 this.close(); this.editor.focus();
219 this.close(); this.editor.focus();
236 //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
237 var that = this;
221 var that = this;
238 setTimeout(function(){that.carryOnCompletion();}, 50);
222 setTimeout(function(){that.carryOnCompletion();}, 50);
239 }
223 }
240 }
224 }
241
225
242
226
243 IPython.Completer = Completer;
227 IPython.Completer = Completer;
244
228
245 return IPython;
229 return IPython;
246 }(IPython));
230 }(IPython));
General Comments 0
You need to be logged in to leave comments. Login now