##// END OF EJS Templates
Notebook now uses tab for autocompletion.
Brian E. Granger -
Show More
@@ -1,303 +1,313
1 1
2 2 //============================================================================
3 3 // CodeCell
4 4 //============================================================================
5 5
6 6 var IPython = (function (IPython) {
7 7
8 8 var utils = IPython.utils;
9 9
10 10 var CodeCell = function (notebook) {
11 11 this.code_mirror = null;
12 12 this.input_prompt_number = ' ';
13 13 this.is_completing = false;
14 14 this.completion_cursor = null;
15 15 IPython.Cell.apply(this, arguments);
16 16 };
17 17
18 18
19 19 CodeCell.prototype = new IPython.Cell();
20 20
21 21
22 22 CodeCell.prototype.create_element = function () {
23 23 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell vbox');
24 24 var input = $('<div></div>').addClass('input hbox');
25 25 input.append($('<div/>').addClass('prompt input_prompt'));
26 26 var input_area = $('<div/>').addClass('input_area box-flex1');
27 27 this.code_mirror = CodeMirror(input_area.get(0), {
28 28 indentUnit : 4,
29 29 enterMode : 'flat',
30 30 tabMode: 'shift',
31 31 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this)
32 32 });
33 33 input.append(input_area);
34 34 var output = $('<div></div>').addClass('output vbox');
35 35 cell.append(input).append(output);
36 36 this.element = cell;
37 37 this.collapse()
38 38 };
39 39
40 40
41 41 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
42 42 // This method gets called in CodeMirror's onKeyDown/onKeyPress handlers and
43 43 // is used to provide custom key handling. Its return value is used to determine
44 44 // if CodeMirror should ignore the event: true = ignore, false = don't ignore.
45 45 if (event.keyCode === 13 && event.shiftKey) {
46 46 // Always ignore shift-enter in CodeMirror as we handle it.
47 47 return true;
48 } else if (event.keyCode == 32 && (event.ctrlKey || event.metaKey) && !event.altKey) {
49 event.stop();
48 // } else if (event.keyCode == 32 && (event.ctrlKey || event.metaKey) && !event.altKey) {
49 } else if (event.keyCode == 9) {
50 50 var cur = editor.getCursor();
51 var line = editor.getLine(cur.line);
52 this.is_completing = true;
53 this.completion_cursor = cur;
54 IPython.notebook.complete_cell(this, line, cur.ch);
51 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur).trim();
52 if (pre_cursor === "") {
53 // Don't autocomplete if the part of the line before the cursor is empty.
54 // In this case, let CodeMirror handle indentation.
55 return false;
56 } else {
57 // Autocomplete the current line.
58 event.stop();
59 var line = editor.getLine(cur.line);
60 this.is_completing = true;
61 this.completion_cursor = cur;
62 IPython.notebook.complete_cell(this, line, cur.ch);
63 return true;
64 }
55 65 } else {
56 66 if (this.is_completing && this.completion_cursor !== editor.getCursor()) {
57 67 this.is_completing = false;
58 68 this.completion_cursor = null;
59 69 console.log("Stopped completing early");
60 70 }
61 71 return false;
62 72 };
63 73 };
64 74
65 75
66 76 CodeCell.prototype.finish_completing = function (matched_text, matches) {
67 77 if (!this.is_completing || matches.length === 0) {return;}
68 78 // console.log("Got matches", matched_text, matches);
69 79
70 80 var that = this;
71 81 var cur = this.completion_cursor;
72 82 var complete = $('<div/>').addClass('completions');
73 83 var select = $('<select/>').attr('multiple','true');
74 84 for (var i=0; i<matches.length; ++i) {
75 85 select.append($('<option/>').text(matches[i]));
76 86 }
77 87 select.children().first().attr('selected','true');
78 88 select.attr('size',Math.min(10,matches.length));
79 89 var pos = this.code_mirror.cursorCoords();
80 90 complete.css('left',pos.x+'px');
81 91 complete.css('top',pos.yBot+'px');
82 92 complete.append(select);
83 93
84 94 $('body').append(complete);
85 95 var done = false;
86 96
87 97 var insert = function (selected_text) {
88 98 that.code_mirror.replaceRange(
89 99 selected_text,
90 100 {line: cur.line, ch: (cur.ch-matched_text.length)},
91 101 {line: cur.line, ch: cur.ch}
92 102 );
93 103 };
94 104
95 105 var close = function () {
96 106 if (done) return;
97 107 done = true;
98 108 complete.remove();
99 109 that.is_completing = false;
100 110 that.completion_cursor = null;
101 111 };
102 112
103 113 var pick = function () {
104 114 insert(select.val()[0]);
105 115 close();
106 116 setTimeout(function(){that.code_mirror.focus();}, 50);
107 117 };
108 118
109 119 select.blur(close);
110 120 select.keydown(function (event) {
111 121 var code = event.which;
112 122 if (code === 13 || code === 32) {
113 123 // Pressing SPACE or ENTER will cause a pick
114 124 event.stopPropagation();
115 125 event.preventDefault();
116 126 pick();
117 127 } else if (code === 38 || code === 40) {
118 128 // We don't want the document keydown handler to handle UP/DOWN,
119 129 // but we want the default action.
120 130 event.stopPropagation();
121 131 } else {
122 132 // All other key presses simple exit completion.
123 133 event.stopPropagation();
124 134 event.preventDefault();
125 135 close();
126 136 that.code_mirror.focus();
127 137 }
128 138 });
129 139 // Double click also causes a pick.
130 140 select.dblclick(pick);
131 141 select.focus();
132 142 };
133 143
134 144
135 145 CodeCell.prototype.select = function () {
136 146 IPython.Cell.prototype.select.apply(this);
137 147 this.code_mirror.focus();
138 148 };
139 149
140 150
141 151 CodeCell.prototype.append_pyout = function (data, n) {
142 152 var toinsert = $("<div/>").addClass("output_area output_pyout hbox");
143 153 toinsert.append($('<div/>').
144 154 addClass('prompt output_prompt').
145 155 html('Out[' + n + ']:')
146 156 );
147 157 this.append_display_data(data, toinsert);
148 158 toinsert.children().last().addClass("box_flex1");
149 159 this.element.find("div.output").append(toinsert);
150 160 // If we just output latex, typeset it.
151 161 if (data["text/latex"] !== undefined) {
152 162 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
153 163 };
154 164 };
155 165
156 166
157 167 CodeCell.prototype.append_pyerr = function (ename, evalue, tb) {
158 168 var s = '';
159 169 var len = tb.length;
160 170 for (var i=0; i<len; i++) {
161 171 s = s + tb[i] + '\n';
162 172 }
163 173 s = s + '\n';
164 174 this.append_stream(s);
165 175 };
166 176
167 177
168 178 CodeCell.prototype.append_display_data = function (data, element) {
169 179 if (data["text/latex"] !== undefined) {
170 180 this.append_latex(data["text/latex"], element);
171 181 // If it is undefined, then we just appended to div.output, which
172 182 // makes the latex visible and we can typeset it. The typesetting
173 183 // has to be done after the latex is on the page.
174 184 if (element === undefined) {
175 185 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
176 186 };
177 187 } else if (data["image/svg+xml"] !== undefined) {
178 188 this.append_svg(data["image/svg+xml"], element);
179 189 } else if (data["image/png"] !== undefined) {
180 190 this.append_png(data["image/png"], element);
181 191 } else if (data["text/plain"] !== undefined) {
182 192 this.append_stream(data["text/plain"], element);
183 193 };
184 194 return element;
185 195 };
186 196
187 197
188 198 CodeCell.prototype.append_stream = function (data, element) {
189 199 element = element || this.element.find("div.output");
190 200 var toinsert = $("<div/>").addClass("output_area output_stream");
191 201 toinsert.append($("<pre/>").html(utils.fixConsole(data)));
192 202 element.append(toinsert);
193 203 return element;
194 204 };
195 205
196 206
197 207 CodeCell.prototype.append_svg = function (svg, element) {
198 208 element = element || this.element.find("div.output");
199 209 var toinsert = $("<div/>").addClass("output_area output_svg");
200 210 toinsert.append(svg);
201 211 element.append(toinsert);
202 212 return element;
203 213 };
204 214
205 215
206 216 CodeCell.prototype.append_png = function (png, element) {
207 217 element = element || this.element.find("div.output");
208 218 var toinsert = $("<div/>").addClass("output_area output_png");
209 219 toinsert.append($("<img/>").attr('src','data:image/png;base64,'+png));
210 220 element.append(toinsert);
211 221 return element;
212 222 };
213 223
214 224
215 225 CodeCell.prototype.append_latex = function (latex, element) {
216 226 // This method cannot do the typesetting because the latex first has to
217 227 // be on the page.
218 228 element = element || this.element.find("div.output");
219 229 var toinsert = $("<div/>").addClass("output_area output_latex");
220 230 toinsert.append(latex);
221 231 element.append(toinsert);
222 232 return element;
223 233 }
224 234
225 235
226 236 CodeCell.prototype.clear_output = function () {
227 237 this.element.find("div.output").html("");
228 238 };
229 239
230 240
231 241 CodeCell.prototype.clear_input = function () {
232 242 this.code_mirror.setValue('');
233 243 };
234 244
235 245
236 246 CodeCell.prototype.collapse = function () {
237 247 this.element.find('div.output').hide();
238 248 };
239 249
240 250
241 251 CodeCell.prototype.expand = function () {
242 252 this.element.find('div.output').show();
243 253 };
244 254
245 255
246 256 CodeCell.prototype.set_input_prompt = function (number) {
247 257 var n = number || ' ';
248 258 this.input_prompt_number = n
249 259 this.element.find('div.input_prompt').html('In&nbsp;[' + n + ']:');
250 260 };
251 261
252 262
253 263 CodeCell.prototype.get_code = function () {
254 264 return this.code_mirror.getValue();
255 265 };
256 266
257 267
258 268 CodeCell.prototype.set_code = function (code) {
259 269 return this.code_mirror.setValue(code);
260 270 };
261 271
262 272
263 273 CodeCell.prototype.at_top = function () {
264 274 var cursor = this.code_mirror.getCursor();
265 275 if (cursor.line === 0) {
266 276 return true;
267 277 } else {
268 278 return false;
269 279 }
270 280 };
271 281
272 282
273 283 CodeCell.prototype.at_bottom = function () {
274 284 var cursor = this.code_mirror.getCursor();
275 285 if (cursor.line === (this.code_mirror.lineCount()-1)) {
276 286 return true;
277 287 } else {
278 288 return false;
279 289 }
280 290 };
281 291
282 292
283 293 CodeCell.prototype.fromJSON = function (data) {
284 294 if (data.cell_type === 'code') {
285 295 this.set_code(data.code);
286 296 this.set_input_prompt(data.prompt_number);
287 297 };
288 298 };
289 299
290 300
291 301 CodeCell.prototype.toJSON = function () {
292 302 return {
293 303 code : this.get_code(),
294 304 cell_type : 'code',
295 305 prompt_number : this.input_prompt_number
296 306 };
297 307 };
298 308
299 309 IPython.CodeCell = CodeCell;
300 310
301 311 return IPython;
302 312 }(IPython));
303 313
General Comments 0
You need to be logged in to leave comments. Login now