Show More
@@ -0,0 +1,233 | |||
|
1 | // function completer. | |
|
2 | // | |
|
3 | // completer should be a class that take an editor instance, and a list of | |
|
4 | // function to call to get the list of completion. | |
|
5 | // | |
|
6 | // the function that send back the list of completion should received the | |
|
7 | // editor handle as sole argument, and should return a json object with the | |
|
8 | // following structure | |
|
9 | ||
|
10 | // {list: clist, # list of n string containing the completions | |
|
11 | // type : rp, # list of n string containingtype/ origin of the completion | |
|
12 | // # (will be set as the class of the <option> to be able to style | |
|
13 | // # them according to the origin of the completion) | |
|
14 | // from: {line: cur.line, ch: token.start}, | |
|
15 | // to: {line: cur.line, ch: token.end} | |
|
16 | // }; | |
|
17 | // | |
|
18 | ||
|
19 | var IPython = (function(IPython ) { | |
|
20 | // that will prevent us froom missspelling | |
|
21 | "use strict"; | |
|
22 | ||
|
23 | // easyier key mapping | |
|
24 | var key = { tab:9, | |
|
25 | esc:27, | |
|
26 | backspace:8, | |
|
27 | space:32, | |
|
28 | shift:16, | |
|
29 | enter:13, | |
|
30 | upArrow:38, // check with keyDown.. | |
|
31 | downArrow :40 // check with keyUp | |
|
32 | }; | |
|
33 | ||
|
34 | // what is the common start of all completions | |
|
35 | function sharedStart(B){ | |
|
36 | if(B.length == 1){return B[0]} | |
|
37 | var A = new Array() | |
|
38 | for(i=0; i< B.length; i++) | |
|
39 | { | |
|
40 | A.push(B[i].str); | |
|
41 | } | |
|
42 | if(A.length > 1 ){ | |
|
43 | var tem1, tem2, s, A = A.slice(0).sort(); | |
|
44 | tem1 = A[0]; | |
|
45 | s = tem1.length; | |
|
46 | tem2 = A.pop(); | |
|
47 | while(s && tem2.indexOf(tem1) == -1){ | |
|
48 | tem1 = tem1.substring(0, --s); | |
|
49 | } | |
|
50 | return { str : tem1, | |
|
51 | type : "computed", | |
|
52 | from : B[0].from, | |
|
53 | to : B[0].to | |
|
54 | }; | |
|
55 | } | |
|
56 | return null; | |
|
57 | } | |
|
58 | ||
|
59 | // user to nsert the given completion | |
|
60 | var Completer = function(getHints) { | |
|
61 | ||
|
62 | this.hintfunc = getHints; | |
|
63 | // if last caractere before cursor is not in this, we stop completing | |
|
64 | this.reg = /[A-Za-z.]/; | |
|
65 | } | |
|
66 | ||
|
67 | Completer.prototype.startCompletionFor = function(ed) | |
|
68 | { | |
|
69 | // call for a 'first' completion, that will set the editor and do some | |
|
70 | // special behaviour like autopicking if only one completion availlable | |
|
71 | // | |
|
72 | this.editor = ed; | |
|
73 | if (this.editor.somethingSelected()) return; | |
|
74 | this.done = false; | |
|
75 | // use to get focus back on opera | |
|
76 | this.carryOnCompletion(true); | |
|
77 | } | |
|
78 | ||
|
79 | Completer.prototype.carryOnCompletion = function(ff) | |
|
80 | { | |
|
81 | // pass true as parameter if you want the commpleter to autopick | |
|
82 | // when only one completion | |
|
83 | // as this function is auto;atically reinvoked at each keystroke with | |
|
84 | // ff = false | |
|
85 | var cur = this.editor.getCursor(); | |
|
86 | var pre_cursor = this.editor.getRange({line:cur.line,ch:cur.ch-1},cur); | |
|
87 | ||
|
88 | // we nned to check that we are still on a word boundary | |
|
89 | // because while typing the completer is still reinvoking itself | |
|
90 | if(!this.reg.test(pre_cursor)){ this.close(); return;} | |
|
91 | ||
|
92 | this.autopick = false; | |
|
93 | if( ff != 'undefined' && ff==true) | |
|
94 | { | |
|
95 | this.autopick=true; | |
|
96 | } | |
|
97 | // We want a single cursor position. | |
|
98 | if (this.editor.somethingSelected()) return; | |
|
99 | ||
|
100 | // there we will need to gather the results for all the function (and merge them ?) | |
|
101 | // lets assume for now only one source | |
|
102 | // | |
|
103 | var that = this; | |
|
104 | this.hintfunc(this.editor,function(result){that._resume_completion(result)}); | |
|
105 | } | |
|
106 | Completer.prototype._resume_completion = function(results) | |
|
107 | { | |
|
108 | this.raw_result = results; | |
|
109 | ||
|
110 | // if empty result return | |
|
111 | if (!this.raw_result || !this.raw_result.length) return; | |
|
112 | ||
|
113 | ||
|
114 | ||
|
115 | // When there is only one completion, use it directly. | |
|
116 | if (this.autopick == true && this.raw_result.length == 1) | |
|
117 | { | |
|
118 | this.insert(this.raw_result[0]); | |
|
119 | return true; | |
|
120 | } | |
|
121 | ||
|
122 | if (this.raw_result.length == 1) | |
|
123 | { | |
|
124 | // test if first and only completion totally matches | |
|
125 | // what is typed, in this case dismiss | |
|
126 | var str = this.raw_result[0].str | |
|
127 | var cur = this.editor.getCursor(); | |
|
128 | var pre_cursor = this.editor.getRange({line:cur.line,ch:cur.ch-str.length},cur); | |
|
129 | if(pre_cursor == str){ | |
|
130 | this.close(); | |
|
131 | return ; | |
|
132 | } | |
|
133 | } | |
|
134 | ||
|
135 | this.complete = $('<div/>').addClass('completions'); | |
|
136 | this.complete.attr('id','complete'); | |
|
137 | ||
|
138 | this.sel = $('<select/>').attr('multiple','true'); | |
|
139 | var pos = this.editor.cursorCoords(); | |
|
140 | ||
|
141 | // TODO: I propose to remove enough horizontal pixel | |
|
142 | // to align the text later | |
|
143 | this.complete.css('left',pos.x+'px'); | |
|
144 | this.complete.css('top',pos.yBot+'px'); | |
|
145 | this.complete.append(this.sel); | |
|
146 | ||
|
147 | $('body').append(this.complete); | |
|
148 | //build the container | |
|
149 | var that = this; | |
|
150 | this.sel.dblclick(function(){that.pick()}); | |
|
151 | this.sel.blur(this.close); | |
|
152 | this.sel.keydown(function(event){that.keydown(event)}); | |
|
153 | ||
|
154 | this.build_gui_list(this.raw_result); | |
|
155 | var that=this; | |
|
156 | //CodeMirror.connect(that.sel, "dblclick", function(){that.pick()}); | |
|
157 | ||
|
158 | this.sel.focus(); | |
|
159 | // Opera sometimes ignores focusing a freshly created node | |
|
160 | if (window.opera) setTimeout(function(){if (!this.done) this.sel.focus();}, 100); | |
|
161 | // why do we return true ? | |
|
162 | return true; | |
|
163 | } | |
|
164 | ||
|
165 | Completer.prototype.insert = function(completion) { | |
|
166 | this.editor.replaceRange(completion.str, completion.from, completion.to); | |
|
167 | } | |
|
168 | ||
|
169 | Completer.prototype.build_gui_list = function(completions){ | |
|
170 | // Need to clear the all list | |
|
171 | for (var i = 0; i < completions.length; ++i) { | |
|
172 | var opt = $('<option/>') | |
|
173 | .text(completions[i].str) | |
|
174 | .addClass(completions[i].type); | |
|
175 | this.sel.append(opt); | |
|
176 | } | |
|
177 | this.sel.children().first().attr('selected','true'); | |
|
178 | ||
|
179 | //sel.size = Math.min(10, completions.length); | |
|
180 | // Hack to hide the scrollbar. | |
|
181 | //if (completions.length <= 10) | |
|
182 | //this.complete.style.width = (this.sel.clientWidth - 1) + "px"; | |
|
183 | } | |
|
184 | ||
|
185 | Completer.prototype.close = function() { | |
|
186 | if (this.done) return; | |
|
187 | this.done = true; | |
|
188 | $('.completions').remove(); | |
|
189 | } | |
|
190 | ||
|
191 | Completer.prototype.pick = function(){ | |
|
192 | this.insert(this.raw_result[this.sel[0].selectedIndex]); | |
|
193 | this.close(); | |
|
194 | var that = this; | |
|
195 | setTimeout(function(){that.editor.focus();}, 50); | |
|
196 | } | |
|
197 | ||
|
198 | ||
|
199 | Completer.prototype.keydown = function(event) { | |
|
200 | var code = event.keyCode; | |
|
201 | // Enter | |
|
202 | if (code == key.enter) {CodeMirror.e_stop(event); this.pick();} | |
|
203 | // Escape or backspace | |
|
204 | else if (code == key.esc ) {CodeMirror.e_stop(event); this.close(); this.editor.focus();} | |
|
205 | else if (code == key.space || code == key.backspace) {this.close(); this.editor.focus();} | |
|
206 | else if (code == key.tab){ | |
|
207 | //all the fastforwarding operation, | |
|
208 | this.insert(sharedStart(this.raw_result)); | |
|
209 | this.close(); | |
|
210 | CodeMirror.e_stop(event); | |
|
211 | this.editor.focus(); | |
|
212 | //reinvoke self | |
|
213 | var that = this; | |
|
214 | setTimeout(function(){that.carryOnCompletion();}, 50); | |
|
215 | } | |
|
216 | else if (code == key.upArrow || code == key.downArrow) { | |
|
217 | // need to do that to be able to move the arrow | |
|
218 | // when on the first or last line ofo a code cell | |
|
219 | event.stopPropagation(); | |
|
220 | } | |
|
221 | else if (code != key.upArrow && code != key.downArrow) { | |
|
222 | this.close(); this.editor.focus(); | |
|
223 | //we give focus to the editor immediately and call sell in 50 ms | |
|
224 | var that = this; | |
|
225 | setTimeout(function(){that.carryOnCompletion();}, 50); | |
|
226 | } | |
|
227 | } | |
|
228 | ||
|
229 | ||
|
230 | IPython.Completer = Completer; | |
|
231 | ||
|
232 | return IPython; | |
|
233 | }(IPython)); |
@@ -0,0 +1,89 | |||
|
1 | // highly adapted for codemiror jshint | |
|
2 | ||
|
3 | (function () { | |
|
4 | function forEach(arr, f) { | |
|
5 | for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]); | |
|
6 | } | |
|
7 | ||
|
8 | function arrayContains(arr, item) { | |
|
9 | if (!Array.prototype.indexOf) { | |
|
10 | var i = arr.length; | |
|
11 | while (i--) { | |
|
12 | if (arr[i] === item) { | |
|
13 | return true; | |
|
14 | } | |
|
15 | } | |
|
16 | return false; | |
|
17 | } | |
|
18 | return arr.indexOf(item) != -1; | |
|
19 | } | |
|
20 | ||
|
21 | CodeMirror.contextHint = function(editor) { | |
|
22 | // Find the token at the cursor | |
|
23 | var cur = editor.getCursor(), token = editor.getTokenAt(cur), tprop = token; | |
|
24 | // If it's not a 'word-style' token, ignore the token. | |
|
25 | // If it is a property, find out what it is a property of. | |
|
26 | ||
|
27 | var list = new Array(); | |
|
28 | var clist = getCompletions(token,editor) ; | |
|
29 | for( var i = 0 ; i < clist.length ; i++) | |
|
30 | { | |
|
31 | list.push( | |
|
32 | { | |
|
33 | str : clist[i], | |
|
34 | type : "context", | |
|
35 | from : {line: cur.line, ch: token.start}, | |
|
36 | to : {line: cur.line, ch: token.end} | |
|
37 | } | |
|
38 | ) | |
|
39 | ||
|
40 | } | |
|
41 | return list; | |
|
42 | } | |
|
43 | ||
|
44 | // find all 'words' of current cell | |
|
45 | function getAllTokens(editor) | |
|
46 | { | |
|
47 | var found = []; | |
|
48 | // get all text remove and split it before dot and at space | |
|
49 | // keep the dot for completing token that also start with dot | |
|
50 | var candidates = editor.getValue() | |
|
51 | .replace(/[. ]/g,"\n") | |
|
52 | .split('\n'); | |
|
53 | // append to arry if not already (the function) | |
|
54 | function maybeAdd(str) { | |
|
55 | if (!arrayContains(found, str)) found.push(str); | |
|
56 | } | |
|
57 | ||
|
58 | // append to arry if not already | |
|
59 | // (here we do it ) | |
|
60 | for( c in candidates ) | |
|
61 | { | |
|
62 | if(candidates[c].length >= 1){ | |
|
63 | maybeAdd(candidates[c]);} | |
|
64 | } | |
|
65 | return found; | |
|
66 | ||
|
67 | } | |
|
68 | ||
|
69 | function getCompletions(token,editor) | |
|
70 | { | |
|
71 | var candidates = getAllTokens(editor); | |
|
72 | // filter all token that have a common start (but nox exactly) the lenght of the current token | |
|
73 | var prependchar =''; | |
|
74 | if(token.string.indexOf('.') == 0) | |
|
75 | { | |
|
76 | prependchar = '.' | |
|
77 | } | |
|
78 | var lambda = function(x){ | |
|
79 | x = prependchar+x; | |
|
80 | return (x.indexOf(token.string)==0 && x != token.string)}; | |
|
81 | var filterd = candidates.filter(lambda); | |
|
82 | for( var i in filterd) | |
|
83 | { | |
|
84 | // take care of reappending '.' at the beginning | |
|
85 | filterd[i] = prependchar+filterd[i]; | |
|
86 | } | |
|
87 | return filterd; | |
|
88 | } | |
|
89 | })(); |
@@ -273,6 +273,13 div.text_cell_render { | |||
|
273 | 273 | font-family: monospace; |
|
274 | 274 | } |
|
275 | 275 | |
|
276 | option.context { | |
|
277 | background-color: #DEF7FF; | |
|
278 | } | |
|
279 | option.introspection { | |
|
280 | background-color: #EBF4EB; | |
|
281 | } | |
|
282 | ||
|
276 | 283 | @-moz-keyframes fadeIn { |
|
277 | 284 | from {opacity:0;} |
|
278 | 285 | to {opacity:1;} |
@@ -10,7 +10,6 | |||
|
10 | 10 | //============================================================================ |
|
11 | 11 | |
|
12 | 12 | var IPython = (function (IPython) { |
|
13 | ||
|
14 | 13 | var utils = IPython.utils; |
|
15 | 14 | |
|
16 | 15 | var CodeCell = function (notebook) { |
@@ -23,6 +22,8 var IPython = (function (IPython) { | |||
|
23 | 22 | this.tooltip_timeout = null; |
|
24 | 23 | this.clear_out_timeout = null; |
|
25 | 24 | IPython.Cell.apply(this, arguments); |
|
25 | var that = this; | |
|
26 | this.ccc = new IPython.Completer(function(ed, callback){that.requestCompletion(ed, callback)}); | |
|
26 | 27 | }; |
|
27 | 28 | |
|
28 | 29 | |
@@ -40,8 +41,11 var IPython = (function (IPython) { | |||
|
40 | 41 | mode: 'python', |
|
41 | 42 | theme: 'ipython', |
|
42 | 43 | readOnly: this.read_only, |
|
43 | onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this) | |
|
44 | onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this), | |
|
44 | 45 | }); |
|
46 | var that = this; | |
|
47 | ccm = this.code_mirror; | |
|
48 | ccc = this.ccc; | |
|
45 | 49 | input.append(input_area); |
|
46 | 50 | var output = $('<div></div>').addClass('output vbox'); |
|
47 | 51 | cell.append(input).append(output); |
@@ -129,13 +133,9 var IPython = (function (IPython) { | |||
|
129 | 133 | // Prevent CodeMirror from handling the tab. |
|
130 | 134 | return true; |
|
131 | 135 | } else { |
|
132 | pre_cursor.trim(); | |
|
133 | // Autocomplete the current line. | |
|
134 | 136 | event.stop(); |
|
135 | var line = editor.getLine(cur.line); | |
|
136 | this.is_completing = true; | |
|
137 | this.completion_cursor = cur; | |
|
138 | IPython.notebook.complete_cell(this, line, cur.ch); | |
|
137 | this.ccc.startCompletionFor(this.code_mirror); | |
|
138 | ||
|
139 | 139 | return true; |
|
140 | 140 | }; |
|
141 | 141 | } else if (event.keyCode === 8 && event.type == 'keydown') { |
@@ -263,285 +263,39 var IPython = (function (IPython) { | |||
|
263 | 263 | }; |
|
264 | 264 | |
|
265 | 265 | // As you type completer |
|
266 |
CodeCell.prototype. |
|
|
267 | if(matched_text[0]=='%'){ | |
|
268 | completing_from_magic = true; | |
|
269 | completing_to_magic = false; | |
|
270 | } else { | |
|
271 | completing_from_magic = false; | |
|
272 | completing_to_magic = false; | |
|
273 | } | |
|
274 | //return if not completing or nothing to complete | |
|
275 |
|
|
|
276 | ||
|
277 | // for later readability | |
|
278 | var key = { tab:9, | |
|
279 | esc:27, | |
|
280 | backspace:8, | |
|
281 | space:32, | |
|
282 | shift:16, | |
|
283 | enter:13, | |
|
284 | // _ is 95 | |
|
285 | isCompSymbol : function (code) | |
|
286 | { | |
|
287 | return (code > 64 && code <= 90) | |
|
288 | || (code >= 97 && code <= 122) | |
|
289 | || (code == 95) | |
|
290 | }, | |
|
291 | dismissAndAppend : function (code) | |
|
292 | { | |
|
293 | chararr = '()[]+-/\\. ,=*'.split(""); | |
|
294 | codearr = chararr.map(function(x){return x.charCodeAt(0)}); | |
|
295 | return jQuery.inArray(code, codearr) != -1; | |
|
296 | } | |
|
297 | ||
|
298 | } | |
|
299 | ||
|
300 | // smart completion, sort kwarg ending with '=' | |
|
301 | var newm = new Array(); | |
|
302 | if(this.notebook.smart_completer) | |
|
303 | { | |
|
304 | kwargs = new Array(); | |
|
305 | other = new Array(); | |
|
306 | for(var i = 0 ; i<matches.length ; ++i){ | |
|
307 | if(matches[i].substr(-1) === '='){ | |
|
308 | kwargs.push(matches[i]); | |
|
309 | }else{other.push(matches[i]);} | |
|
310 | } | |
|
311 | newm = kwargs.concat(other); | |
|
312 | matches = newm; | |
|
313 | } | |
|
314 | // end sort kwargs | |
|
315 | ||
|
316 | // give common prefix of a array of string | |
|
317 | function sharedStart(A){ | |
|
318 | shared=''; | |
|
319 | if(A.length == 1){shared=A[0]} | |
|
320 | if(A.length > 1 ){ | |
|
321 | var tem1, tem2, s, A = A.slice(0).sort(); | |
|
322 | tem1 = A[0]; | |
|
323 | s = tem1.length; | |
|
324 | tem2 = A.pop(); | |
|
325 | while(s && tem2.indexOf(tem1) == -1){ | |
|
326 | tem1 = tem1.substring(0, --s); | |
|
327 | } | |
|
328 | shared = tem1; | |
|
329 | } | |
|
330 | if (shared[0] == '%' && !completing_from_magic) | |
|
331 | { | |
|
332 | shared = shared.substr(1); | |
|
333 | return [shared, true]; | |
|
334 | } else { | |
|
335 | return [shared, false]; | |
|
336 | } | |
|
337 | } | |
|
338 | ||
|
339 | ||
|
340 | //try to check if the user is typing tab at least twice after a word | |
|
341 | // and completion is "done" | |
|
342 | fallback_on_tooltip_after = 2 | |
|
343 | if(matches.length == 1 && matched_text === matches[0]) | |
|
344 | { | |
|
345 | if(this.npressed >fallback_on_tooltip_after && this.prevmatch==matched_text) | |
|
346 | { | |
|
347 | this.request_tooltip_after_time(matched_text+'(',0); | |
|
348 | return; | |
|
349 | } | |
|
350 | this.prevmatch = matched_text | |
|
351 | this.npressed = this.npressed+1; | |
|
352 | } | |
|
353 | else | |
|
354 | { | |
|
355 | this.prevmatch = ""; | |
|
356 | this.npressed = 0; | |
|
357 | } | |
|
358 | // end fallback on tooltip | |
|
359 | //================================== | |
|
360 | // Real completion logic start here | |
|
361 | var that = this; | |
|
362 | var cur = this.completion_cursor; | |
|
363 | var done = false; | |
|
364 | ||
|
365 | // call to dismmiss the completer | |
|
366 | var close = function () { | |
|
367 | if (done) return; | |
|
368 | done = true; | |
|
369 | if (complete != undefined) | |
|
370 | {complete.remove();} | |
|
371 | that.is_completing = false; | |
|
372 | that.completion_cursor = null; | |
|
373 | }; | |
|
374 | ||
|
375 | // update codemirror with the typed text | |
|
376 | prev = matched_text | |
|
377 | var update = function (inserted_text, event) { | |
|
378 | that.code_mirror.replaceRange( | |
|
379 | inserted_text, | |
|
380 | {line: cur.line, ch: (cur.ch-matched_text.length)}, | |
|
381 | {line: cur.line, ch: (cur.ch+prev.length-matched_text.length)} | |
|
382 | ); | |
|
383 | prev = inserted_text | |
|
384 | if(event != null){ | |
|
385 | event.stopPropagation(); | |
|
386 | event.preventDefault(); | |
|
387 | } | |
|
388 | }; | |
|
389 | // insert the given text and exit the completer | |
|
390 | var insert = function (selected_text, event) { | |
|
391 | update(selected_text) | |
|
392 | close(); | |
|
393 | setTimeout(function(){that.code_mirror.focus();}, 50); | |
|
394 | }; | |
|
395 | ||
|
396 | // insert the curent highlited selection and exit | |
|
397 | var pick = function () { | |
|
398 | insert(select.val()[0],null); | |
|
399 | }; | |
|
400 | ||
|
266 | CodeCell.prototype.requestCompletion= function(ed,callback) | |
|
267 | { | |
|
268 | this._compcallback = callback; | |
|
269 | this._editor = ed; | |
|
270 | var cur = ed.getCursor(); | |
|
271 | var pre_cursor = this.code_mirror.getRange({line:cur.line,ch:0},cur); | |
|
272 | pre_cursor.trim(); | |
|
273 | // Autocomplete the current line. | |
|
274 | var line = this.code_mirror.getLine(cur.line); | |
|
275 | this.is_completing = true; | |
|
276 | this.completion_cursor = cur; | |
|
277 | IPython.notebook.complete_cell(this, line, cur.ch); | |
|
278 | } | |
|
401 | 279 | |
|
402 | // Define function to clear the completer, refill it with the new | |
|
403 | // matches, update the pseuso typing field. autopick insert match if | |
|
404 | // only one left, in no matches (anymore) dismiss itself by pasting | |
|
405 | // what the user have typed until then | |
|
406 | var complete_with = function(matches,typed_text,autopick,event) | |
|
280 | CodeCell.prototype.finish_completing = function (matched_text, matches) { | |
|
281 | // let's build a function that wrap all that stuff into what is needed for the | |
|
282 | // new completer: | |
|
283 | // | |
|
284 | var cur = this._editor.getCursor(); | |
|
285 | res = CodeMirror.contextHint(this._editor); | |
|
286 | for( i=0; i< matches.length ; i++) | |
|
407 | 287 | { |
|
408 | // If autopick an only one match, past. | |
|
409 | // Used to 'pick' when pressing tab | |
|
410 | var prefix = ''; | |
|
411 | if(completing_to_magic && !completing_from_magic) | |
|
412 | { | |
|
413 | prefix='%'; | |
|
414 | } | |
|
415 | if (matches.length < 1) { | |
|
416 | insert(prefix+typed_text,event); | |
|
417 | if(event != null){ | |
|
418 | event.stopPropagation(); | |
|
419 | event.preventDefault(); | |
|
420 | } | |
|
421 | } else if (autopick && matches.length == 1) { | |
|
422 | insert(matches[0],event); | |
|
423 | if(event != null){ | |
|
424 | event.stopPropagation(); | |
|
425 | event.preventDefault(); | |
|
426 | } | |
|
427 | return; | |
|
428 | } | |
|
429 | //clear the previous completion if any | |
|
430 | update(prefix+typed_text,event); | |
|
431 | complete.children().children().remove(); | |
|
432 | $('#asyoutype').html("<b>"+prefix+matched_text+"</b>"+typed_text.substr(matched_text.length)); | |
|
433 | select = $('#asyoutypeselect'); | |
|
434 | for (var i = 0; i<matches.length; ++i) { | |
|
435 | select.append($('<option/>').html(matches[i])); | |
|
436 | } | |
|
437 | select.children().first().attr('selected','true'); | |
|
438 | } | |
|
439 | ||
|
440 | // create html for completer | |
|
441 | var complete = $('<div/>').addClass('completions'); | |
|
442 | complete.attr('id','complete'); | |
|
443 | complete.append($('<p/>').attr('id', 'asyoutype').html('<b>fixed part</b>user part'));//pseudo input field | |
|
444 | ||
|
445 | var select = $('<select/>').attr('multiple','true'); | |
|
446 | select.attr('id', 'asyoutypeselect') | |
|
447 | select.attr('size',Math.min(10,matches.length)); | |
|
448 | var pos = this.code_mirror.cursorCoords(); | |
|
449 | ||
|
450 | // TODO: I propose to remove enough horizontal pixel | |
|
451 | // to align the text later | |
|
452 | complete.css('left',pos.x+'px'); | |
|
453 | complete.css('top',pos.yBot+'px'); | |
|
454 | complete.append(select); | |
|
455 | ||
|
456 | $('body').append(complete); | |
|
457 | ||
|
458 | // So a first actual completion. see if all the completion start wit | |
|
459 | // the same letter and complete if necessary | |
|
460 | ff = sharedStart(matches) | |
|
461 | fastForward = ff[0]; | |
|
462 | completing_to_magic = ff[1]; | |
|
463 | typed_characters = fastForward.substr(matched_text.length); | |
|
464 | complete_with(matches,matched_text+typed_characters,true,null); | |
|
465 | filterd = matches; | |
|
466 | // Give focus to select, and make it filter the match as the user type | |
|
467 | // by filtering the previous matches. Called by .keypress and .keydown | |
|
468 | var downandpress = function (event,press_or_down) { | |
|
469 | var code = event.which; | |
|
470 | var autopick = false; // auto 'pick' if only one match | |
|
471 | if (press_or_down === 0){ | |
|
472 | press = true; down = false; //Are we called from keypress or keydown | |
|
473 | } else if (press_or_down == 1){ | |
|
474 | press = false; down = true; | |
|
475 | } | |
|
476 | if (code === key.shift) { | |
|
477 | // nothing on Shift | |
|
478 | return; | |
|
479 | } | |
|
480 | if (key.dismissAndAppend(code) && press) { | |
|
481 | var newchar = String.fromCharCode(code); | |
|
482 | typed_characters = typed_characters+newchar; | |
|
483 | insert(matched_text+typed_characters,event); | |
|
484 | return | |
|
485 | } | |
|
486 | if (code === key.enter) { | |
|
487 | // Pressing ENTER will cause a pick | |
|
488 | event.stopPropagation(); | |
|
489 | event.preventDefault(); | |
|
490 | pick(); | |
|
491 | } else if (code === 38 || code === 40) { | |
|
492 | // We don't want the document keydown handler to handle UP/DOWN, | |
|
493 | // but we want the default action. | |
|
494 | event.stopPropagation(); | |
|
495 | } else if ( (code == key.backspace)||(code == key.tab && down) || press || key.isCompSymbol(code)){ | |
|
496 | if( key.isCompSymbol(code) && press) | |
|
288 | res.push( | |
|
497 | 289 | { |
|
498 | var newchar = String.fromCharCode(code); | |
|
499 | typed_characters = typed_characters+newchar; | |
|
500 | } else if (code == key.tab) { | |
|
501 | ff = sharedStart(matches) | |
|
502 | fastForward = ff[0]; | |
|
503 | completing_to_magic = ff[1]; | |
|
504 | ffsub = fastForward.substr(matched_text.length+typed_characters.length); | |
|
505 | typed_characters = typed_characters+ffsub; | |
|
506 | autopick = true; | |
|
507 | } else if (code == key.backspace && down) { | |
|
508 | // cancel if user have erase everything, otherwise decrease | |
|
509 | // what we filter with | |
|
510 | event.preventDefault(); | |
|
511 | if (typed_characters.length <= 0) | |
|
512 | { | |
|
513 | insert(matched_text,event) | |
|
514 | return | |
|
515 | } | |
|
516 | typed_characters = typed_characters.substr(0,typed_characters.length-1); | |
|
517 | } else if (press && code != key.backspace && code != key.tab && code != 0){ | |
|
518 | insert(matched_text+typed_characters,event); | |
|
519 | return | |
|
520 | } else { | |
|
521 | return | |
|
290 | str : matches[i], | |
|
291 | type : "introspection", | |
|
292 | from : {line: cur.line, ch: cur.ch-matched_text.length}, | |
|
293 | to : {line: cur.line, ch: cur.ch} | |
|
522 | 294 | } |
|
523 | re = new RegExp("^"+"\%?"+matched_text+typed_characters,""); | |
|
524 | filterd = matches.filter(function(x){return re.test(x)}); | |
|
525 | ff = sharedStart(filterd); | |
|
526 | completing_to_magic = ff[1]; | |
|
527 | complete_with(filterd,matched_text+typed_characters,autopick,event); | |
|
528 | } else if (code == key.esc) { | |
|
529 | // dismiss the completer and go back to before invoking it | |
|
530 | insert(matched_text,event); | |
|
531 | } else if (press) { // abort only on .keypress or esc | |
|
532 | } | |
|
295 | ) | |
|
533 | 296 | } |
|
534 | select.keydown(function (event) { | |
|
535 | downandpress(event,1) | |
|
536 | }); | |
|
537 | select.keypress(function (event) { | |
|
538 | downandpress(event,0) | |
|
539 | }); | |
|
540 | // Double click also causes a pick. | |
|
541 | // and bind the last actions. | |
|
542 | select.dblclick(pick); | |
|
543 | select.blur(close); | |
|
544 | select.focus(); | |
|
297 | this._compcallback(res); | |
|
298 | ||
|
545 | 299 | }; |
|
546 | 300 | |
|
547 | 301 |
@@ -1,5 +1,4 | |||
|
1 | 1 | {% extends page.html %} |
|
2 | ||
|
3 | 2 | {% block stylesheet %} |
|
4 | 3 | |
|
5 | 4 | {% if mathjax_url %} |
@@ -220,6 +219,7 data-notebook-id={{notebook_id}} | |||
|
220 | 219 | <script src="{{ static_url("js/initmathjax.js") }}" type="text/javascript" charset="utf-8"></script> |
|
221 | 220 | <script src="{{ static_url("js/cell.js") }}" type="text/javascript" charset="utf-8"></script> |
|
222 | 221 | <script src="{{ static_url("js/codecell.js") }}" type="text/javascript" charset="utf-8"></script> |
|
222 | <script src="{{ static_url("js/completer.js") }}" type="text/javascript" charset="utf-8"></script> | |
|
223 | 223 | <script src="{{ static_url("js/textcell.js") }}" type="text/javascript" charset="utf-8"></script> |
|
224 | 224 | <script src="{{ static_url("js/kernel.js") }}" type="text/javascript" charset="utf-8"></script> |
|
225 | 225 | <script src="{{ static_url("js/savewidget.js") }}" type="text/javascript" charset="utf-8"></script> |
@@ -231,5 +231,8 data-notebook-id={{notebook_id}} | |||
|
231 | 231 | <script src="{{ static_url("js/notificationwidget.js") }}" type="text/javascript" charset="utf-8"></script> |
|
232 | 232 | <script src="{{ static_url("js/notebookmain.js") }}" type="text/javascript" charset="utf-8"></script> |
|
233 | 233 | |
|
234 | <script src="{{ static_url("js/context-hint.js") }} charset="utf-8"></script> | |
|
235 | <script src="{{ static_url("codemirror/lib/util/simple-hint.js") }} charset="utf-8"></script> | |
|
236 | ||
|
234 | 237 | {% end %} |
|
235 | 238 |
General Comments 0
You need to be logged in to leave comments.
Login now