##// END OF EJS Templates
Clean code, retab and minor fix...
Matthias BUSSONNIER -
Show More
@@ -1,232 +1,231 b''
1 1 // function completer.
2 2 //
3 3 // completer should be a class that take an cell instance
4 4
5 5 var IPython = (function(IPython ) {
6 6 // that will prevent us from misspelling
7 7 "use strict";
8 8
9 9 // easyier key mapping
10 10 var key = IPython.utils.keycodes;
11 11
12 12 // what is the common start of all completions
13 13 function sharedStart(B){
14 if(B.length == 1){return B[0]}
15 var A = new Array()
14 if(B.length == 1){return B[0];}
15 var A = new Array();
16 16 for(var i=0; i< B.length; i++)
17 17 {
18 18 A.push(B[i].str);
19 19 }
20 20 if(A.length > 1 ){
21 var tem1, tem2, s, A = A.slice(0).sort();
21 var tem1, tem2, s;
22 A = A.slice(0).sort();
22 23 tem1 = A[0];
23 24 s = tem1.length;
24 25 tem2 = A.pop();
25 26 while(s && tem2.indexOf(tem1) == -1){
26 27 tem1 = tem1.substring(0, --s);
27 28 }
28 29 if (tem1 == "" || tem2.indexOf(tem1) != 0){return null;}
29 30 return { str : tem1,
30 31 type : "computed",
31 32 from : B[0].from,
32 33 to : B[0].to
33 34 };
34 35 }
35 36 return null;
36 37 }
37 38
38 39
39 40 var Completer = function(cell) {
40 41 this.editor = cell.code_mirror;
41 42 // if last caractere before cursor is not in this, we stop completing
42 43 this.reg = /[0-9a-z.]/i; // casse insensitive
43 }
44 };
44 45
45 46 Completer.prototype.kernelCompletionRequest = function(){
46 47 var cur = this.editor.getCursor();
47 48 var line = this.editor.getLine(cur.line);
48 49 // one could fork here and directly call finish completing if kernel is busy
49 50 var callbacks = {'complete_reply': $.proxy(this.finish_completing,this)};
50 51 IPython.notebook.kernel.complete(line, cur.ch, callbacks);
51 }
52 };
52 53
53 54
54 55 Completer.prototype.startCompletion = function()
55 56 {
56 57 // call for a 'first' completion, that will set the editor and do some
57 58 // special behaviour like autopicking if only one completion availlable
58 59 //
59 60 if (this.editor.somethingSelected()) return;
60 61 this.done = false;
61 62 // use to get focus back on opera
62 63 this.carryOnCompletion(true);
63 }
64 };
64 65
65 66 Completer.prototype.carryOnCompletion = function(ff) {
66 67 // Pass true as parameter if you want the commpleter to autopick when
67 68 // only one completion. This function is automatically reinvoked at
68 69 // each keystroke with ff = false
69 70
70 71 var cur = this.editor.getCursor();
71 72 var pre_cursor = this.editor.getRange({line:cur.line,ch:cur.ch-1},cur);
72 73
73 74 // we need to check that we are still on a word boundary
74 75 // because while typing the completer is still reinvoking itself
75 76 if(!this.reg.test(pre_cursor)){ this.close(); return;}
76 77
77 78 this.autopick = false;
78 79 if( ff != 'undefined' && ff==true)
79 80 { this.autopick=true; }
80 81
81 82 // We want a single cursor position.
82 83 if (this.editor.somethingSelected()) return;
83 84
84 85 //one kernel completion came back, finish_completing will be called with the results
85 86 this.kernelCompletionRequest();
86 }
87 };
87 88
88 89 Completer.prototype.finish_completing =function (content) {
89 90 // let's build a function that wrap all that stuff into what is needed
90 91 // for the new completer:
91 92 var matched_text = content.matched_text;
92 93 var matches = content.matches;
93 94
94 95 var cur = this.editor.getCursor();
95 96 var results = CodeMirror.contextHint(this.editor);
96 97
97 98 // append the introspection result, in order, at at the beginning of
98 99 // the table and compute the replacement range from current cursor
99 100 // positon and matched_text length.
100 101 for(var i= matches.length-1; i>=0 ;--i)
101 102 {
102 103 results.unshift(
103 104 { str : matches[i],
104 105 type : "introspection",
105 106 from : {line: cur.line, ch: cur.ch-matched_text.length},
106 107 to : {line: cur.line, ch: cur.ch}
107 })
108 });
108 109 }
109 110
110 111 // one the 2 sources results have been merge, deal with it
111 112 this.raw_result = results;
112 113
113 114 // if empty result return
114 115 if (!this.raw_result || !this.raw_result.length) return;
115 116
116 117 // When there is only one completion, use it directly.
117 118 if (this.autopick == true && this.raw_result.length == 1)
118 119 {
119 120 this.insert(this.raw_result[0]);
120 return true;
121 return;
121 122 }
122 123
123 124 if (this.raw_result.length == 1)
124 125 {
125 126 // test if first and only completion totally matches
126 127 // what is typed, in this case dismiss
127 var str = this.raw_result[0].str
128 var cur = this.editor.getCursor();
128 var str = this.raw_result[0].str;
129 129 var pre_cursor = this.editor.getRange({line:cur.line,ch:cur.ch-str.length},cur);
130 130 if(pre_cursor == str)
131 131 { this.close(); return ; }
132 132 }
133 133
134 134 this.complete = $('<div/>').addClass('completions');
135 135 this.complete.attr('id','complete');
136 136
137 137 this.sel = $('<select/>')
138 138 .attr('multiple','true')
139 139 .attr('size',Math.min(10,this.raw_result.length));
140 140 var pos = this.editor.cursorCoords();
141 141
142 142 // TODO: I propose to remove enough horizontal pixel
143 143 // to align the text later
144 144 this.complete.css('left',pos.x+'px');
145 145 this.complete.css('top',pos.yBot+'px');
146 146 this.complete.append(this.sel);
147 147
148 148 $('body').append(this.complete);
149 149 //build the container
150 150 var that = this;
151 this.sel.dblclick(function(){that.pick()});
151 this.sel.dblclick(function(){that.pick();});
152 152 this.sel.blur(this.close);
153 this.sel.keydown(function(event){that.keydown(event)});
153 this.sel.keydown(function(event){that.keydown(event);});
154 154
155 155 this.build_gui_list(this.raw_result);
156 156
157 157 this.sel.focus();
158 158 // Opera sometimes ignores focusing a freshly created node
159 159 if (window.opera) setTimeout(function(){if (!this.done) this.sel.focus();}, 100);
160 160 return true;
161 161 }
162 162
163 163 Completer.prototype.insert = function(completion) {
164 164 this.editor.replaceRange(completion.str, completion.from, completion.to);
165 165 }
166 166
167 167 Completer.prototype.build_gui_list = function(completions){
168 168 // Need to clear the all list
169 169 for (var i = 0; i < completions.length; ++i) {
170 170 var opt = $('<option/>')
171 171 .text(completions[i].str)
172 172 .addClass(completions[i].type);
173 173 this.sel.append(opt);
174 174 }
175 175 this.sel.children().first().attr('selected','true');
176 176 }
177 177
178 178 Completer.prototype.close = function() {
179 179 if (this.done) return;
180 180 this.done = true;
181 181 $('.completions').remove();
182 182 }
183 183
184 184 Completer.prototype.pick = function(){
185 185 this.insert(this.raw_result[this.sel[0].selectedIndex]);
186 186 this.close();
187 187 var that = this;
188 188 setTimeout(function(){that.editor.focus();}, 50);
189 189 }
190 190
191 191
192 192 Completer.prototype.keydown = function(event) {
193 193 var code = event.keyCode;
194 var that = this;
194 195 // Enter
195 196 if (code == key.enter) {CodeMirror.e_stop(event); this.pick();}
196 197 // Escape or backspace
197 198 else if (code == key.esc ) {CodeMirror.e_stop(event); this.close(); this.editor.focus();}
198 199 else if (code == key.space || code == key.backspace) {this.close(); this.editor.focus();}
199 200 else if (code == key.tab){
200 201 //all the fastforwarding operation,
201 202 //Check that shared start is not null which can append with prefixed completion
202 203 // like %pylab , pylab have no shred start, and ff will result in py<tab><tab>
203 204 // to erase py
204 205 var sh = sharedStart(this.raw_result);
205 206 if(sh){
206 207 this.insert(sh);
207 208 }
208 209 this.close();
209 210 CodeMirror.e_stop(event);
210 211 this.editor.focus();
211 212 //reinvoke self
212 var that = this;
213 213 setTimeout(function(){that.carryOnCompletion();}, 50);
214 214 }
215 215 else if (code == key.upArrow || code == key.downArrow) {
216 216 // need to do that to be able to move the arrow
217 217 // when on the first or last line ofo a code cell
218 218 event.stopPropagation();
219 219 }
220 220 else if (code != key.upArrow && code != key.downArrow) {
221 221 this.close(); this.editor.focus();
222 222 //we give focus to the editor immediately and call sell in 50 ms
223 var that = this;
224 223 setTimeout(function(){that.carryOnCompletion();}, 50);
225 224 }
226 225 }
227 226
228 227
229 228 IPython.Completer = Completer;
230 229
231 230 return IPython;
232 231 }(IPython));
1 NO CONTENT: modified file
@@ -1,297 +1,294 b''
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2008-2011 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // Tooltip
10 10 //============================================================================
11 11 //
12 // you can set the autocall time by setting `IPython.notebook.time_before_tooltip` in ms
12 // you can set the autocall time by setting `IPython.tooltip.time_before_tooltip` in ms
13 13
14 14 var IPython = (function (IPython) {
15 15
16 16 var utils = IPython.utils;
17 17
18 18 // tooltip constructor
19 19 var Tooltip = function () {
20 20 var that = this;
21 21 this.time_before_tooltip = 1200;
22 22
23 23 // handle to html
24 24 this.tooltip = $('#tooltip');
25 25 var tooltip = this.tooltip;
26 26 this._hidden = true;
27 27
28 28 // variable for consecutive call
29 29 this._old_cell = null ;
30 30 this._old_request = null ;
31 31 this._consecutive_conter = 0;
32 32
33 33 // 'sticky ?'
34 34 this._sticky = false;
35 35
36 36 // contain the button in the upper right corner
37 37 this.buttons = $('<div/>')
38 38 .addClass('tooltipbuttons');
39 39
40 40 // will contain the docstring
41 41 this.text = $('<div/>')
42 42 .addClass('tooltiptext')
43 43 .addClass('smalltooltip');
44 44
45 45 // build the buttons menu on the upper right
46 46
47 47 // expand the tooltip to see more
48 48 var expandlink=$('<a/>').attr('href',"#")
49 49 .addClass("ui-corner-all") //rounded corner
50 50 .attr('role',"button")
51 51 .attr('id','expanbutton')
52 52 .click(function(){that.expand()})
53 53 .append(
54 54 $('<span/>').text('Expand')
55 55 .addClass('ui-icon')
56 56 .addClass('ui-icon-plus')
57 57 );
58 58
59 59 // open in pager
60 60 var morelink=$('<a/>').attr('href',"#")
61 61 .attr('role',"button")
62 62 .addClass('ui-button');
63 63 var morespan=$('<span/>').text('Open in Pager')
64 64 .addClass('ui-icon')
65 65 .addClass('ui-icon-arrowstop-l-n');
66 66 morelink.append(morespan);
67 67 morelink.click(function(){
68 68 that.showInPager();
69 69 });
70 70
71 71 // close the tooltip
72 72 var closelink=$('<a/>').attr('href',"#");
73 73 closelink.attr('role',"button");
74 74 closelink.addClass('ui-button');
75 75 var closespan=$('<span/>').text('Close');
76 76 closespan.addClass('ui-icon');
77 77 closespan.addClass('ui-icon-close');
78 78 closelink.append(closespan);
79 79 closelink.click(function(){
80 80 that.remove_and_cancel_tooltip(true);
81 81 });
82 82
83 83 //construct the tooltip
84 84 // add in the reverse order you want them to appear
85 85 this.buttons.append(closelink);
86 86 this.buttons.append(expandlink);
87 87 this.buttons.append(morelink);
88 88
89 89 // we need a phony element to make the small arrow
90 90 // of the tooltip in css
91 91 // we will move the arrow later
92 92 this.arrow = $('<div/>').addClass('pretooltiparrow');
93 93 this.tooltip.append(this.buttons);
94 94 this.tooltip.append(this.arrow);
95 95 this.tooltip.append(this.text);
96 96 };
97 97
98 // will resend the request on behalf on the cell which invoked the tooltip
99 // to show in it in pager. This is done so to be sure of having the same
100 // result as invoking `something?`
101 98 Tooltip.prototype.showInPager = function()
102 99 {
103 100 var that = this;
104 101 var callbacks = {'execute_reply': $.proxy(that._handle_execute_reply,that)}
105 102 var msg_id = IPython.notebook.kernel.execute(this.name+"?", callbacks);
106 103
107 104 this.remove_and_cancel_tooltip();
108 105 this._cmfocus();
109 106 }
110 107
111 108 // grow the tooltip verticaly
112 109 Tooltip.prototype.expand = function(){
113 110 this.text.removeClass('smalltooltip');
114 111 this.text.addClass('bigtooltip');
115 112 $('#expanbutton').addClass('hidden');
116 113 this._cmfocus();
117 114 }
118 115
119 116 // deal with all the logic of hiding the tooltip
120 117 // and reset it's status
121 118 Tooltip.prototype.hide = function()
122 119 {
123 120 this.tooltip.addClass('hide');
124 121 $('#expanbutton').removeClass('hidden');
125 122 this.text.removeClass('bigtooltip');
126 123 this.text.addClass('smalltooltip');
127 124 // keep scroll top to be sure to always see the first line
128 125 this.text.scrollTop(0);
129 126 this._hidden = true;
130 127 }
131 128
132 129 Tooltip.prototype.remove_and_cancel_tooltip = function(force) {
133 130 // note that we don't handle closing directly inside the calltip
134 131 // as in the completer, because it is not focusable, so won't
135 132 // get the event.
136 133 if(this._sticky == false || force == true)
137 134 {
138 135 this.hide();
139 136 }
140 137 this.cancel_pending();
141 138 this._old_cell = null ;
142 139 this._old_request = null ;
143 140 this._consecutive_conter = 0;
144 141 }
145 142
146 143 // cancel autocall done after '(' for example.
147 144 Tooltip.prototype.cancel_pending = function(){
148 145 if (this.tooltip_timeout != null){
149 146 clearTimeout(this.tooltip_timeout);
150 147 this.tooltip_timeout = null;
151 148 }
152 149 }
153 150
154 151 // will trigger tooltip after timeout
155 152 Tooltip.prototype.pending = function(cell,text)
156 153 {
157 154 var that = this;
158 155 this.tooltip_timeout = setTimeout(function(){that.request(cell)} , that.time_before_tooltip);
159 156 }
160 157
161 158 // make an imediate completion request
162 159 Tooltip.prototype.request = function(cell)
163 160 {
164 161 this.cancel_pending();
165 162 var editor = cell.code_mirror;
166 163 this.code_mirror = editor;
167 164 var cursor = editor.getCursor();
168 165 var text = editor.getRange({line:cursor.line,ch:0},cursor).trim();
169 166
170 167 if( this._old_cell == cell && this._old_request == text && this._hidden == false)
171 168 {
172 169 this._consecutive_conter = this._consecutive_conter +1;
173 170 } else {
174 171 this._old_cell = cell ;
175 172 this._old_request = text ;
176 173 this._consecutive_conter =0;
177 174 this.cancel_stick();
178 175 }
179 176
180 177 if( this._consecutive_conter == 1 )
181 178 {
182 179 this.expand()
183 180 return;
184 181 }
185 182 else if( this._consecutive_conter == 2)
186 183 {
187 184 this.stick();
188 185 return;
189 186 }
190 187 else if( this._consecutive_conter == 3)
191 188 {
192 189 this._old_cell = null ;
193 190 this.cancel_stick();
194 191 this._old_request = null ;
195 192 this._consecutive_conter = 0;
196 193 this.showInPager();
197 194 this._cmfocus();
198 195 return;
199 196 }
200 197 else if( this._consecutive_conter == 4)
201 198 {
202 199
203 200 }
204 201
205 202 if (text === "" || text === "(" ) {
206 203 return;
207 204 // don't do anything if line beggin with '(' or is empty
208 205 }
209 206 cell.request_tooltip(text);
210 207 }
211 208
212 209 // cancel the option of having the tooltip to stick
213 210 Tooltip.prototype.cancel_stick = function()
214 211 {
215 212 clearTimeout(this._stick_timeout);
216 213 this._sticky = false;
217 214 }
218 215
219 216 // put the tooltip in a sicky state for 10 seconds
220 217 // it won't be removed by remove_and_cancell() unless you called with
221 218 // the first parameter set to true.
222 219 // remove_and_cancell_tooltip(true)
223 220 Tooltip.prototype.stick = function()
224 221 {
225 222 var that = this;
226 223 this._sticky = true;
227 224 this._stick_timeout = setTimeout( function(){
228 225 that._sticky = false;
229 226 }, 10*1000
230 227 );
231 228 }
232 229
233 230 // should be called with the kernel reply to actually show the tooltip
234 231 Tooltip.prototype.show = function(reply, codecell)
235 232 {
236 233 // move the bubble if it is not hidden
237 234 // otherwise fade it
238 235 var editor = codecell.code_mirror;
239 236 this.name = reply.name;
240 237 this.code_mirror = editor;
241 238
242 239 // do some math to have the tooltip arrow on more or less on left or right
243 240 // width of the editor
244 241 var w= $(this.code_mirror.getScrollerElement()).width();
245 242 // ofset of the editor
246 243 var o= $(this.code_mirror.getScrollerElement()).offset();
247 244 var pos = editor.cursorCoords();
248 245 var xinit = pos.x;
249 246 var xinter = o.left + (xinit-o.left)/w*(w-450);
250 247 var posarrowleft = xinit - xinter;
251 248
252 249
253 250 if( this._hidden == false)
254 251 {
255 252 this.tooltip.animate({'left' : xinter-30+'px','top' :(pos.yBot+10)+'px'});
256 253 } else
257 254 {
258 255 this.tooltip.css({'left' : xinter-30+'px'});
259 256 this.tooltip.css({'top' :(pos.yBot+10)+'px'});
260 257 }
261 258 this.arrow.animate({'left' : posarrowleft+'px'});
262 259 this.tooltip.removeClass('hidden')
263 260 this.tooltip.removeClass('hide');
264 261 this._hidden = false;
265 262
266 263 // build docstring
267 264 defstring = reply.call_def;
268 265 if (defstring == null) { defstring = reply.init_definition; }
269 266 if (defstring == null) { defstring = reply.definition; }
270 267
271 268 docstring = reply.call_docstring;
272 269 if (docstring == null) { docstring = reply.init_docstring; }
273 270 if (docstring == null) { docstring = reply.docstring; }
274 271 if (docstring == null) { docstring = "<empty docstring>"; }
275 272
276 273 this.text.children().remove();
277 274
278 275 var pre=$('<pre/>').html(utils.fixConsole(docstring));
279 276 if(defstring){
280 277 var defstring_html = $('<pre/>').html(utils.fixConsole(defstring));
281 278 this.text.append(defstring_html);
282 279 }
283 280 this.text.append(pre);
284 281 // keep scroll top to be sure to always see the first line
285 282 this.text.scrollTop(0);
286 283 }
287 284
288 285 // convenient funciton to have the correct codemirror back into focus
289 286 Tooltip.prototype._cmfocus = function()
290 287 {
291 288 var cm = this.code_mirror;
292 289 setTimeout(function(){cm.focus();}, 50);
293 290 }
294 291
295 292 IPython.Tooltip = Tooltip;
296 293 return IPython;
297 294 }(IPython));
General Comments 0
You need to be logged in to leave comments. Login now