##// END OF EJS Templates
Merge pull request #2728 from Carreau/shifttab...
Bussonnier Matthias -
r8971:99339d10 merge
parent child Browse files
Show More
@@ -1,369 +1,378
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2008-2011 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // CodeCell
10 10 //============================================================================
11 11 /**
12 12 * An extendable module that provide base functionnality to create cell for notebook.
13 13 * @module IPython
14 14 * @namespace IPython
15 15 * @submodule CodeCell
16 16 */
17 17
18 18 var IPython = (function (IPython) {
19 19 "use strict";
20 20
21 21 var utils = IPython.utils;
22 22 var key = IPython.utils.keycodes;
23 23 CodeMirror.modeURL = "/static/codemirror/mode/%N/%N.js";
24 24
25 25 /**
26 26 * A Cell conceived to write code.
27 27 *
28 28 * The kernel doesn't have to be set at creation time, in that case
29 29 * it will be null and set_kernel has to be called later.
30 30 * @class CodeCell
31 31 * @extends IPython.Cell
32 32 *
33 33 * @constructor
34 34 * @param {Object|null} kernel
35 35 */
36 36 var CodeCell = function (kernel) {
37 37 this.kernel = kernel || null;
38 38 this.code_mirror = null;
39 39 this.input_prompt_number = null;
40 this.tooltip_on_tab = true;
41 40 this.collapsed = false;
42 41 this.default_mode = 'python';
43 42 IPython.Cell.apply(this, arguments);
44 43
45 44 var that = this;
46 45 this.element.focusout(
47 46 function() { that.auto_highlight(); }
48 47 );
49 48 };
50 49
51
52 50 CodeCell.prototype = new IPython.Cell();
53 51
54 52 /**
55 53 * @method auto_highlight
56 54 */
57 55 CodeCell.prototype.auto_highlight = function () {
58 56 this._auto_highlight(IPython.config.cell_magic_highlight)
59 57 };
60 58
61 59 /** @method create_element */
62 60 CodeCell.prototype.create_element = function () {
63 61 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell vbox');
64 62 cell.attr('tabindex','2');
65 63 var input = $('<div></div>').addClass('input hbox');
66 64 input.append($('<div/>').addClass('prompt input_prompt'));
67 65 var input_area = $('<div/>').addClass('input_area box-flex1');
68 66 this.code_mirror = CodeMirror(input_area.get(0), {
69 67 indentUnit : 4,
70 68 mode: 'python',
71 69 theme: 'ipython',
72 70 readOnly: this.read_only,
73 71 extraKeys: {"Tab": "indentMore","Shift-Tab" : "indentLess",'Backspace':"delSpaceToPrevTabStop"},
74 72 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this),
75 73 matchBrackets: true
76 74 });
77 75 input.append(input_area);
78 76 var output = $('<div></div>');
79 77 cell.append(input).append(output);
80 78 this.element = cell;
81 79 this.output_area = new IPython.OutputArea(output, true);
82 80
83 81 // construct a completer only if class exist
84 82 // otherwise no print view
85 83 if (IPython.Completer !== undefined)
86 84 {
87 85 this.completer = new IPython.Completer(this);
88 86 }
89 87 };
90 88
91 89 /**
92 90 * This method gets called in CodeMirror's onKeyDown/onKeyPress
93 91 * handlers and is used to provide custom key handling. Its return
94 92 * value is used to determine if CodeMirror should ignore the event:
95 93 * true = ignore, false = don't ignore.
96 94 * @method handle_codemirror_keyevent
97 95 */
98 96 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
99 97
100 98 if (this.read_only){
101 99 return false;
102 100 }
103 101
104 102 var that = this;
105 103 // whatever key is pressed, first, cancel the tooltip request before
106 104 // they are sent, and remove tooltip if any, except for tab again
107 105 if (event.type === 'keydown' && event.which != key.TAB ) {
108 106 IPython.tooltip.remove_and_cancel_tooltip();
109 107 };
110 108
111 109 var cur = editor.getCursor();
112 110 if (event.keyCode === key.ENTER){
113 111 this.auto_highlight();
114 112 }
115 113
116 114 if (event.keyCode === key.ENTER && (event.shiftKey || event.ctrlKey)) {
117 115 // Always ignore shift-enter in CodeMirror as we handle it.
118 116 return true;
119 117 } else if (event.which === 40 && event.type === 'keypress' && IPython.tooltip.time_before_tooltip >= 0) {
120 118 // triger on keypress (!) otherwise inconsistent event.which depending on plateform
121 119 // browser and keyboard layout !
122 120 // Pressing '(' , request tooltip, don't forget to reappend it
123 121 IPython.tooltip.pending(that);
124 122 } else if (event.which === key.UPARROW && event.type === 'keydown') {
125 123 // If we are not at the top, let CM handle the up arrow and
126 124 // prevent the global keydown handler from handling it.
127 125 if (!that.at_top()) {
128 126 event.stop();
129 127 return false;
130 128 } else {
131 129 return true;
132 130 };
133 131 } else if (event.which === key.ESC) {
134 132 IPython.tooltip.remove_and_cancel_tooltip(true);
135 133 return true;
136 134 } else if (event.which === key.DOWNARROW && event.type === 'keydown') {
137 135 // If we are not at the bottom, let CM handle the down arrow and
138 136 // prevent the global keydown handler from handling it.
139 137 if (!that.at_bottom()) {
140 138 event.stop();
141 139 return false;
142 140 } else {
143 141 return true;
144 142 };
143 } else if (event.keyCode === key.TAB && event.type == 'keydown' && event.shiftKey) {
144 if (editor.somethingSelected()){
145 var anchor = editor.getCursor("anchor");
146 var head = editor.getCursor("head");
147 if( anchor.line != head.line){
148 return false;
149 }
150 }
151 IPython.tooltip.request(that);
152 event.stop();
153 return true;
145 154 } else if (event.keyCode === key.TAB && event.type == 'keydown') {
146 155 // Tab completion.
147 156 //Do not trim here because of tooltip
148 157 if (editor.somethingSelected()){return false}
149 158 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
150 159 if (pre_cursor.trim() === "") {
151 160 // Don't autocomplete if the part of the line before the cursor
152 161 // is empty. In this case, let CodeMirror handle indentation.
153 162 return false;
154 } else if ((pre_cursor.substr(-1) === "("|| pre_cursor.substr(-1) === " ") && that.tooltip_on_tab ) {
163 } else if ((pre_cursor.substr(-1) === "("|| pre_cursor.substr(-1) === " ") && IPython.config.tooltip_on_tab ) {
155 164 IPython.tooltip.request(that);
156 165 // Prevent the event from bubbling up.
157 166 event.stop();
158 167 // Prevent CodeMirror from handling the tab.
159 168 return true;
160 169 } else {
161 170 event.stop();
162 171 this.completer.startCompletion();
163 172 return true;
164 173 };
165 174 } else {
166 175 // keypress/keyup also trigger on TAB press, and we don't want to
167 176 // use those to disable tab completion.
168 177 return false;
169 178 };
170 179 return false;
171 180 };
172 181
173 182
174 183 // Kernel related calls.
175 184
176 185 CodeCell.prototype.set_kernel = function (kernel) {
177 186 this.kernel = kernel;
178 187 }
179 188
180 189 /**
181 190 * Execute current code cell to the kernel
182 191 * @method execute
183 192 */
184 193 CodeCell.prototype.execute = function () {
185 194 this.output_area.clear_output(true, true, true);
186 195 this.set_input_prompt('*');
187 196 this.element.addClass("running");
188 197 var callbacks = {
189 198 'execute_reply': $.proxy(this._handle_execute_reply, this),
190 199 'output': $.proxy(this.output_area.handle_output, this.output_area),
191 200 'clear_output': $.proxy(this.output_area.handle_clear_output, this.output_area),
192 201 'set_next_input': $.proxy(this._handle_set_next_input, this)
193 202 };
194 203 var msg_id = this.kernel.execute(this.get_text(), callbacks, {silent: false});
195 204 };
196 205
197 206 /**
198 207 * @method _handle_execute_reply
199 208 * @private
200 209 */
201 210 CodeCell.prototype._handle_execute_reply = function (content) {
202 211 this.set_input_prompt(content.execution_count);
203 212 this.element.removeClass("running");
204 213 $([IPython.events]).trigger('set_dirty.Notebook', {'value': true});
205 214 }
206 215
207 216 CodeCell.prototype._handle_set_next_input = function (text) {
208 217 var data = {'cell': this, 'text': text}
209 218 $([IPython.events]).trigger('set_next_input.Notebook', data);
210 219 }
211 220
212 221 // Basic cell manipulation.
213 222
214 223 CodeCell.prototype.select = function () {
215 224 IPython.Cell.prototype.select.apply(this);
216 225 this.code_mirror.refresh();
217 226 this.code_mirror.focus();
218 227 this.auto_highlight();
219 228 // We used to need an additional refresh() after the focus, but
220 229 // it appears that this has been fixed in CM. This bug would show
221 230 // up on FF when a newly loaded markdown cell was edited.
222 231 };
223 232
224 233
225 234 CodeCell.prototype.select_all = function () {
226 235 var start = {line: 0, ch: 0};
227 236 var nlines = this.code_mirror.lineCount();
228 237 var last_line = this.code_mirror.getLine(nlines-1);
229 238 var end = {line: nlines-1, ch: last_line.length};
230 239 this.code_mirror.setSelection(start, end);
231 240 };
232 241
233 242
234 243 CodeCell.prototype.collapse = function () {
235 244 this.collapsed = true;
236 245 this.output_area.collapse();
237 246 };
238 247
239 248
240 249 CodeCell.prototype.expand = function () {
241 250 this.collapsed = false;
242 251 this.output_area.expand();
243 252 };
244 253
245 254
246 255 CodeCell.prototype.toggle_output = function () {
247 256 this.collapsed = Boolean(1 - this.collapsed);
248 257 this.output_area.toggle_output();
249 258 };
250 259
251 260
252 261 CodeCell.prototype.toggle_output_scroll = function () {
253 262 this.output_area.toggle_scroll();
254 263 };
255 264
256 265
257 266 CodeCell.input_prompt_classical = function (prompt_value, lines_number) {
258 267 var ns = prompt_value || "&nbsp;";
259 268 return 'In&nbsp;[' + ns + ']:'
260 269 };
261 270
262 271 CodeCell.input_prompt_continuation = function (prompt_value, lines_number) {
263 272 var html = [CodeCell.input_prompt_classical(prompt_value, lines_number)];
264 273 for(var i=1; i < lines_number; i++){html.push(['...:'])};
265 274 return html.join('</br>')
266 275 };
267 276
268 277 CodeCell.input_prompt_function = CodeCell.input_prompt_classical;
269 278
270 279
271 280 CodeCell.prototype.set_input_prompt = function (number) {
272 281 var nline = 1
273 282 if( this.code_mirror != undefined) {
274 283 nline = this.code_mirror.lineCount();
275 284 }
276 285 this.input_prompt_number = number;
277 286 var prompt_html = CodeCell.input_prompt_function(this.input_prompt_number, nline);
278 287 this.element.find('div.input_prompt').html(prompt_html);
279 288 };
280 289
281 290
282 291 CodeCell.prototype.clear_input = function () {
283 292 this.code_mirror.setValue('');
284 293 };
285 294
286 295
287 296 CodeCell.prototype.get_text = function () {
288 297 return this.code_mirror.getValue();
289 298 };
290 299
291 300
292 301 CodeCell.prototype.set_text = function (code) {
293 302 return this.code_mirror.setValue(code);
294 303 };
295 304
296 305
297 306 CodeCell.prototype.at_top = function () {
298 307 var cursor = this.code_mirror.getCursor();
299 308 if (cursor.line === 0 && cursor.ch === 0) {
300 309 return true;
301 310 } else {
302 311 return false;
303 312 }
304 313 };
305 314
306 315
307 316 CodeCell.prototype.at_bottom = function () {
308 317 var cursor = this.code_mirror.getCursor();
309 318 if (cursor.line === (this.code_mirror.lineCount()-1) && cursor.ch === this.code_mirror.getLine(cursor.line).length) {
310 319 return true;
311 320 } else {
312 321 return false;
313 322 }
314 323 };
315 324
316 325
317 326 CodeCell.prototype.clear_output = function (stdout, stderr, other) {
318 327 this.output_area.clear_output(stdout, stderr, other);
319 328 };
320 329
321 330
322 331 // JSON serialization
323 332
324 333 CodeCell.prototype.fromJSON = function (data) {
325 334 IPython.Cell.prototype.fromJSON.apply(this, arguments);
326 335 if (data.cell_type === 'code') {
327 336 if (data.input !== undefined) {
328 337 this.set_text(data.input);
329 338 // make this value the starting point, so that we can only undo
330 339 // to this state, instead of a blank cell
331 340 this.code_mirror.clearHistory();
332 341 this.auto_highlight();
333 342 }
334 343 if (data.prompt_number !== undefined) {
335 344 this.set_input_prompt(data.prompt_number);
336 345 } else {
337 346 this.set_input_prompt();
338 347 };
339 348 this.output_area.fromJSON(data.outputs);
340 349 if (data.collapsed !== undefined) {
341 350 if (data.collapsed) {
342 351 this.collapse();
343 352 } else {
344 353 this.expand();
345 354 };
346 355 };
347 356 };
348 357 };
349 358
350 359
351 360 CodeCell.prototype.toJSON = function () {
352 361 var data = IPython.Cell.prototype.toJSON.apply(this);
353 362 data.input = this.get_text();
354 363 data.cell_type = 'code';
355 364 if (this.input_prompt_number) {
356 365 data.prompt_number = this.input_prompt_number;
357 366 };
358 367 var outputs = this.output_area.toJSON();
359 368 data.outputs = outputs;
360 369 data.language = 'python';
361 370 data.collapsed = this.collapsed;
362 371 return data;
363 372 };
364 373
365 374
366 375 IPython.CodeCell = CodeCell;
367 376
368 377 return IPython;
369 378 }(IPython));
@@ -1,74 +1,78
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2012 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // Notebook
10 10 //============================================================================
11 11
12 12 /**
13 13 * @module IPython
14 14 * @namespace IPython
15 15 **/
16 16
17 17 var IPython = (function (IPython) {
18 18 /**
19 19 * A place where some stuff can be confugured.
20 20 *
21 21 * @class config
22 22 * @static
23 23 *
24 24 **/
25 var config = {
25 var default_config = {
26 26 /**
27 27 * Dictionary of object to autodetect highlight mode for code cell.
28 28 * Item of the dictionnary should take the form :
29 29 *
30 30 * key : {'reg':[list_of_regexp]}
31 31 *
32 32 * where `key` will be the code mirror mode name
33 33 * and `list_of_regexp` should be a list of regext that should match
34 34 * the first line of the cell to trigger this mode.
35 35 *
36 36 * if `key` is prefixed by the `magic_` prefix the codemirror `mode`
37 37 * will be applied only at the end of the first line
38 38 *
39 39 * @attribute cell_magic_highlight
40 40 * @example
41 41 * This would trigger javascript mode
42 42 * from the second line if first line start with `%%javascript` or `%%jsmagic`
43 43 *
44 44 * cell_magic_highlight['magic_javascript'] = {'reg':[/^%%javascript/,/^%%jsmagic/]}
45 45 * @example
46 46 * This would trigger javascript mode
47 47 * from the second line if first line start with `var`
48 48 *
49 49 * cell_magic_highlight['javascript'] = {'reg':[/^var/]}
50 50 */
51 51 cell_magic_highlight : {
52 52 'magic_javascript':{'reg':[/^%%javascript/]}
53 53 ,'magic_perl' :{'reg':[/^%%perl/]}
54 54 ,'magic_ruby' :{'reg':[/^%%ruby/]}
55 55 ,'magic_python' :{'reg':[/^%%python3?/]}
56 56 ,'magic_shell' :{'reg':[/^%%bash/]}
57 57 ,'magic_r' :{'reg':[/^%%R/]}
58 58 },
59 59
60 60 /**
61 61 * same as `cell_magic_highlight` but for raw cells
62 62 * @attribute raw_cell_highlight
63 63 */
64 64 raw_cell_highlight : {
65 65 'diff' :{'reg':[/^diff/]}
66 }
66 },
67
68 tooltip_on_tab : true,
67 69 };
68 70
69 IPython.config = config;
71 // use the same method to merge user configuration
72 IPython.config = {};
73 $.extend(IPython.config, default_config);
70 74
71 75 return IPython;
72 76
73 77 }(IPython));
74 78
@@ -1,363 +1,375
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2008-2011 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7 //============================================================================
8 8 // Tooltip
9 9 //============================================================================
10 10 //
11 11 // you can set the autocall time by setting `IPython.tooltip.time_before_tooltip` in ms
12 12 //
13 13 // you can configure the differents action of pressing tab several times in a row by
14 14 // setting/appending different fonction in the array
15 15 // IPython.tooltip.tabs_functions
16 16 //
17 17 // eg :
18 18 // IPython.tooltip.tabs_functions[4] = function (){console.log('this is the action of the 4th tab pressing')}
19 19 //
20 20 var IPython = (function (IPython) {
21 21 "use strict";
22 22
23 23 var utils = IPython.utils;
24 24
25 25 // tooltip constructor
26 26 var Tooltip = function () {
27 27 var that = this;
28 28 this.time_before_tooltip = 1200;
29 29
30 30 // handle to html
31 31 this.tooltip = $('#tooltip');
32 32 this._hidden = true;
33 33
34 34 // variable for consecutive call
35 35 this._old_cell = null;
36 36 this._old_request = null;
37 37 this._consecutive_counter = 0;
38 38
39 39 // 'sticky ?'
40 40 this._sticky = false;
41 41
42 42 // contain the button in the upper right corner
43 43 this.buttons = $('<div/>').addClass('tooltipbuttons');
44 44
45 45 // will contain the docstring
46 46 this.text = $('<div/>').addClass('tooltiptext').addClass('smalltooltip');
47 47
48 48 // build the buttons menu on the upper right
49 49 // expand the tooltip to see more
50 50 var expandlink = $('<a/>').attr('href', "#").addClass("ui-corner-all") //rounded corner
51 51 .attr('role', "button").attr('id', 'expanbutton').attr('title', 'Grow the tooltip vertically (press tab 2 times)').click(function () {
52 52 that.expand()
53 53 }).append(
54 54 $('<span/>').text('Expand').addClass('ui-icon').addClass('ui-icon-plus'));
55 55
56 56 // open in pager
57 57 var morelink = $('<a/>').attr('href', "#").attr('role', "button").addClass('ui-button').attr('title', 'show the current docstring in pager (press tab 4 times)');
58 58 var morespan = $('<span/>').text('Open in Pager').addClass('ui-icon').addClass('ui-icon-arrowstop-l-n');
59 59 morelink.append(morespan);
60 60 morelink.click(function () {
61 61 that.showInPager();
62 62 });
63 63
64 64 // close the tooltip
65 65 var closelink = $('<a/>').attr('href', "#").attr('role', "button").addClass('ui-button');
66 66 var closespan = $('<span/>').text('Close').addClass('ui-icon').addClass('ui-icon-close');
67 67 closelink.append(closespan);
68 68 closelink.click(function () {
69 69 that.remove_and_cancel_tooltip(true);
70 70 });
71 71
72 72 this._clocklink = $('<a/>').attr('href', "#");
73 73 this._clocklink.attr('role', "button");
74 74 this._clocklink.addClass('ui-button');
75 75 this._clocklink.attr('title', 'Tootip is not dismissed while typing for 10 seconds');
76 76 var clockspan = $('<span/>').text('Close');
77 77 clockspan.addClass('ui-icon');
78 78 clockspan.addClass('ui-icon-clock');
79 79 this._clocklink.append(clockspan);
80 80 this._clocklink.click(function () {
81 81 that.cancel_stick();
82 82 });
83 83
84 84
85 85
86 86
87 87 //construct the tooltip
88 88 // add in the reverse order you want them to appear
89 89 this.buttons.append(closelink);
90 90 this.buttons.append(expandlink);
91 91 this.buttons.append(morelink);
92 92 this.buttons.append(this._clocklink);
93 93 this._clocklink.hide();
94 94
95 95
96 96 // we need a phony element to make the small arrow
97 97 // of the tooltip in css
98 98 // we will move the arrow later
99 99 this.arrow = $('<div/>').addClass('pretooltiparrow');
100 100 this.tooltip.append(this.buttons);
101 101 this.tooltip.append(this.arrow);
102 102 this.tooltip.append(this.text);
103 103
104 104 // function that will be called if you press tab 1, 2, 3... times in a row
105 105 this.tabs_functions = [function (cell, text) {
106 106 that._request_tooltip(cell, text);
107 107 }, function () {
108 108 that.expand();
109 109 }, function () {
110 110 that.stick();
111 111 }, function (cell) {
112 112 that.cancel_stick();
113 113 that.showInPager(cell);
114 114 that._cmfocus();
115 115 }];
116 116 // call after all the tabs function above have bee call to clean their effects
117 117 // if necessary
118 118 this.reset_tabs_function = function (cell, text) {
119 119 this._old_cell = (cell) ? cell : null;
120 120 this._old_request = (text) ? text : null;
121 121 this._consecutive_counter = 0;
122 122 }
123 123 };
124 124
125 125 Tooltip.prototype.showInPager = function (cell) {
126 126 // reexecute last call in pager by appending ? to show back in pager
127 127 var that = this;
128 128 var empty = function () {};
129 129 IPython.notebook.kernel.execute(
130 130 that.name + '?', {
131 131 'execute_reply': empty,
132 132 'output': empty,
133 133 'clear_output': empty,
134 134 'cell': cell
135 135 }, {
136 136 'silent': false
137 137 });
138 138 this.remove_and_cancel_tooltip();
139 139 this._cmfocus();
140 140 }
141 141
142 142 // grow the tooltip verticaly
143 143 Tooltip.prototype.expand = function () {
144 144 this.text.removeClass('smalltooltip');
145 145 this.text.addClass('bigtooltip');
146 146 $('#expanbutton').hide('slow');
147 147 this._cmfocus();
148 148 }
149 149
150 150 // deal with all the logic of hiding the tooltip
151 151 // and reset it's status
152 152 Tooltip.prototype._hide = function () {
153 153 this.tooltip.fadeOut('fast');
154 154 $('#expanbutton').show('slow');
155 155 this.text.removeClass('bigtooltip');
156 156 this.text.addClass('smalltooltip');
157 157 // keep scroll top to be sure to always see the first line
158 158 this.text.scrollTop(0);
159 159 this._hidden = true;
160 160 this.code_mirror = null;
161 161 }
162 162
163 163 Tooltip.prototype.remove_and_cancel_tooltip = function (force) {
164 164 // note that we don't handle closing directly inside the calltip
165 165 // as in the completer, because it is not focusable, so won't
166 166 // get the event.
167 167 if (this._sticky == false || force == true) {
168 168 this.cancel_stick();
169 169 this._hide();
170 170 }
171 171 this.cancel_pending();
172 172 this.reset_tabs_function();
173 173 this._cmfocus();
174 174 }
175 175
176 176 // cancel autocall done after '(' for example.
177 177 Tooltip.prototype.cancel_pending = function () {
178 178 if (this._tooltip_timeout != null) {
179 179 clearTimeout(this._tooltip_timeout);
180 180 this._tooltip_timeout = null;
181 181 }
182 182 }
183 183
184 184 // will trigger tooltip after timeout
185 185 Tooltip.prototype.pending = function (cell) {
186 186 var that = this;
187 187 this._tooltip_timeout = setTimeout(function () {
188 188 that.request(cell)
189 189 }, that.time_before_tooltip);
190 190 }
191 191
192 192 Tooltip.prototype._request_tooltip = function (cell, func) {
193 193 // use internally just to make the request to the kernel
194 194 // Feel free to shorten this logic if you are better
195 195 // than me in regEx
196 196 // basicaly you shoul be able to get xxx.xxx.xxx from
197 197 // something(range(10), kwarg=smth) ; xxx.xxx.xxx( firstarg, rand(234,23), kwarg1=2,
198 198 // remove everything between matchin bracket (need to iterate)
199 199 var matchBracket = /\([^\(\)]+\)/g;
200 200 var endBracket = /\([^\(]*$/g;
201 201 var oldfunc = func;
202 202
203 203 func = func.replace(matchBracket, "");
204 204 while (oldfunc != func) {
205 205 oldfunc = func;
206 206 func = func.replace(matchBracket, "");
207 207 }
208 208 // remove everything after last open bracket
209 209 func = func.replace(endBracket, "");
210 210
211 211 var re = /[a-z_][0-9a-z._]+$/gi; // casse insensitive
212 212 var callbacks = {
213 213 'object_info_reply': $.proxy(this._show, this)
214 214 }
215 215 var msg_id = IPython.notebook.kernel.object_info_request(re.exec(func), callbacks);
216 216 }
217 217
218 218 // make an imediate completion request
219 219 Tooltip.prototype.request = function (cell) {
220 220 // request(codecell)
221 221 // Deal with extracting the text from the cell and counting
222 222 // call in a row
223 223 this.cancel_pending();
224 224 var editor = cell.code_mirror;
225 225 var cursor = editor.getCursor();
226 226 var text = editor.getRange({
227 227 line: cursor.line,
228 228 ch: 0
229 229 }, cursor).trim();
230 230
231 if(editor.somethingSelected()){
232 text = editor.getSelection();
233 }
234
231 235 // need a permanent handel to code_mirror for future auto recall
232 236 this.code_mirror = editor;
233 237
234 238 // now we treat the different number of keypress
235 239 // first if same cell, same text, increment counter by 1
236 240 if (this._old_cell == cell && this._old_request == text && this._hidden == false) {
237 241 this._consecutive_counter++;
238 242 } else {
239 243 // else reset
240 244 this.cancel_stick();
241 245 this.reset_tabs_function (cell, text);
242 246 }
243 247
244 248 // don't do anything if line beggin with '(' or is empty
245 249 if (text === "" || text === "(") {
246 250 return;
247 251 }
248 252
249 253 this.tabs_functions[this._consecutive_counter](cell, text);
250 254
251 255 // then if we are at the end of list function, reset
252 256 if (this._consecutive_counter == this.tabs_functions.length) this.reset_tabs_function (cell, text);
253 257
254 258 return;
255 259 }
256 260
257 261 // cancel the option of having the tooltip to stick
258 262 Tooltip.prototype.cancel_stick = function () {
259 263 clearTimeout(this._stick_timeout);
260 264 this._stick_timeout = null;
261 265 this._clocklink.hide('slow');
262 266 this._sticky = false;
263 267 }
264 268
265 269 // put the tooltip in a sicky state for 10 seconds
266 270 // it won't be removed by remove_and_cancell() unless you called with
267 271 // the first parameter set to true.
268 272 // remove_and_cancell_tooltip(true)
269 273 Tooltip.prototype.stick = function (time) {
270 274 time = (time != undefined) ? time : 10;
271 275 var that = this;
272 276 this._sticky = true;
273 277 this._clocklink.show('slow');
274 278 this._stick_timeout = setTimeout(function () {
275 279 that._sticky = false;
276 280 that._clocklink.hide('slow');
277 281 }, time * 1000);
278 282 }
279 283
280 284 // should be called with the kernel reply to actually show the tooltip
281 285 Tooltip.prototype._show = function (reply) {
282 286 // move the bubble if it is not hidden
283 287 // otherwise fade it
284 288 this.name = reply.name;
285 289
286 290 // do some math to have the tooltip arrow on more or less on left or right
287 291 // width of the editor
288 292 var w = $(this.code_mirror.getScrollerElement()).width();
289 293 // ofset of the editor
290 294 var o = $(this.code_mirror.getScrollerElement()).offset();
291 var pos = this.code_mirror.cursorCoords();
295
296 // whatever anchor/head order but arrow at mid x selection
297 var anchor = this.code_mirror.cursorCoords(false);
298 var head = this.code_mirror.cursorCoords(true);
299 var pos = {};
300 pos.y = head.y
301 pos.yBot = head.yBot
302 pos.x = (head.x+anchor.x)/2;
303
292 304 var xinit = pos.x;
293 305 var xinter = o.left + (xinit - o.left) / w * (w - 450);
294 306 var posarrowleft = xinit - xinter;
295 307
296 308
297 309 if (this._hidden == false) {
298 310 this.tooltip.animate({
299 311 'left': xinter - 30 + 'px',
300 312 'top': (pos.yBot + 10) + 'px'
301 313 });
302 314 } else {
303 315 this.tooltip.css({
304 316 'left': xinter - 30 + 'px'
305 317 });
306 318 this.tooltip.css({
307 319 'top': (pos.yBot + 10) + 'px'
308 320 });
309 321 }
310 322 this.arrow.animate({
311 323 'left': posarrowleft + 'px'
312 324 });
313 325 this.tooltip.fadeIn('fast');
314 326 this._hidden = false;
315 327
316 328 // build docstring
317 329 var defstring = reply.call_def;
318 330 if (defstring == null) {
319 331 defstring = reply.init_definition;
320 332 }
321 333 if (defstring == null) {
322 334 defstring = reply.definition;
323 335 }
324 336
325 337 var docstring = reply.call_docstring;
326 338 if (docstring == null) {
327 339 docstring = reply.init_docstring;
328 340 }
329 341 if (docstring == null) {
330 342 docstring = reply.docstring;
331 343 }
332 344 if (docstring == null) {
333 345 docstring = "<empty docstring>";
334 346 }
335 347
336 348 this.text.children().remove();
337 349
338 350 var pre = $('<pre/>').html(utils.fixConsole(docstring));
339 351 if (defstring) {
340 352 var defstring_html = $('<pre/>').html(utils.fixConsole(defstring));
341 353 this.text.append(defstring_html);
342 354 }
343 355 this.text.append(pre);
344 356 // keep scroll top to be sure to always see the first line
345 357 this.text.scrollTop(0);
346 358 }
347 359
348 360 // convenient funciton to have the correct code_mirror back into focus
349 361 Tooltip.prototype._cmfocus = function () {
350 362 var cm = this.code_mirror;
351 363 if (cm == IPython.notebook.get_selected_cell())
352 364 {
353 365 setTimeout(function () {
354 366 cm.focus();
355 367 }, 50);
356 368 }
357 369 }
358 370
359 371 IPython.Tooltip = Tooltip;
360 372
361 373 return IPython;
362 374
363 375 }(IPython));
General Comments 0
You need to be logged in to leave comments. Login now