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 | font-family: monospace; |
|
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 | @-moz-keyframes fadeIn { |
|
283 | @-moz-keyframes fadeIn { | |
277 | from {opacity:0;} |
|
284 | from {opacity:0;} | |
278 | to {opacity:1;} |
|
285 | to {opacity:1;} |
@@ -10,7 +10,6 | |||||
10 | //============================================================================ |
|
10 | //============================================================================ | |
11 |
|
11 | |||
12 | var IPython = (function (IPython) { |
|
12 | var IPython = (function (IPython) { | |
13 |
|
||||
14 | var utils = IPython.utils; |
|
13 | var utils = IPython.utils; | |
15 |
|
14 | |||
16 | var CodeCell = function (notebook) { |
|
15 | var CodeCell = function (notebook) { | |
@@ -23,6 +22,8 var IPython = (function (IPython) { | |||||
23 | this.tooltip_timeout = null; |
|
22 | this.tooltip_timeout = null; | |
24 | this.clear_out_timeout = null; |
|
23 | this.clear_out_timeout = null; | |
25 | IPython.Cell.apply(this, arguments); |
|
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 | mode: 'python', |
|
41 | mode: 'python', | |
41 | theme: 'ipython', |
|
42 | theme: 'ipython', | |
42 | readOnly: this.read_only, |
|
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 | input.append(input_area); |
|
49 | input.append(input_area); | |
46 | var output = $('<div></div>').addClass('output vbox'); |
|
50 | var output = $('<div></div>').addClass('output vbox'); | |
47 | cell.append(input).append(output); |
|
51 | cell.append(input).append(output); | |
@@ -129,13 +133,9 var IPython = (function (IPython) { | |||||
129 | // Prevent CodeMirror from handling the tab. |
|
133 | // Prevent CodeMirror from handling the tab. | |
130 | return true; |
|
134 | return true; | |
131 | } else { |
|
135 | } else { | |
132 | pre_cursor.trim(); |
|
|||
133 | // Autocomplete the current line. |
|
|||
134 | event.stop(); |
|
136 | event.stop(); | |
135 | var line = editor.getLine(cur.line); |
|
137 | this.ccc.startCompletionFor(this.code_mirror); | |
136 | this.is_completing = true; |
|
138 | ||
137 | this.completion_cursor = cur; |
|
|||
138 | IPython.notebook.complete_cell(this, line, cur.ch); |
|
|||
139 | return true; |
|
139 | return true; | |
140 | }; |
|
140 | }; | |
141 | } else if (event.keyCode === 8 && event.type == 'keydown') { |
|
141 | } else if (event.keyCode === 8 && event.type == 'keydown') { | |
@@ -263,285 +263,39 var IPython = (function (IPython) { | |||||
263 | }; |
|
263 | }; | |
264 |
|
264 | |||
265 | // As you type completer |
|
265 | // As you type completer | |
266 |
CodeCell.prototype. |
|
266 | CodeCell.prototype.requestCompletion= function(ed,callback) | |
267 | if(matched_text[0]=='%'){ |
|
267 | { | |
268 | completing_from_magic = true; |
|
268 | this._compcallback = callback; | |
269 | completing_to_magic = false; |
|
269 | this._editor = ed; | |
270 | } else { |
|
270 | var cur = ed.getCursor(); | |
271 | completing_from_magic = false; |
|
271 | var pre_cursor = this.code_mirror.getRange({line:cur.line,ch:0},cur); | |
272 | completing_to_magic = false; |
|
272 | pre_cursor.trim(); | |
273 | } |
|
273 | // Autocomplete the current line. | |
274 | //return if not completing or nothing to complete |
|
274 | var line = this.code_mirror.getLine(cur.line); | |
275 |
|
|
275 | this.is_completing = true; | |
276 |
|
276 | this.completion_cursor = cur; | ||
277 | // for later readability |
|
277 | IPython.notebook.complete_cell(this, line, cur.ch); | |
278 | var key = { tab:9, |
|
278 | } | |
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 |
|
||||
401 |
|
279 | |||
402 | // Define function to clear the completer, refill it with the new |
|
280 | CodeCell.prototype.finish_completing = function (matched_text, matches) { | |
403 | // matches, update the pseuso typing field. autopick insert match if |
|
281 | // let's build a function that wrap all that stuff into what is needed for the | |
404 | // only one left, in no matches (anymore) dismiss itself by pasting |
|
282 | // new completer: | |
405 | // what the user have typed until then |
|
283 | // | |
406 | var complete_with = function(matches,typed_text,autopick,event) |
|
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. |
|
288 | res.push( | |
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) |
|
|||
497 | { |
|
289 | { | |
498 | var newchar = String.fromCharCode(code); |
|
290 | str : matches[i], | |
499 | typed_characters = typed_characters+newchar; |
|
291 | type : "introspection", | |
500 | } else if (code == key.tab) { |
|
292 | from : {line: cur.line, ch: cur.ch-matched_text.length}, | |
501 | ff = sharedStart(matches) |
|
293 | to : {line: cur.line, ch: cur.ch} | |
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 |
|
|||
522 | } |
|
294 | } | |
523 | re = new RegExp("^"+"\%?"+matched_text+typed_characters,""); |
|
295 | ) | |
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 | } |
|
|||
533 | } |
|
296 | } | |
534 | select.keydown(function (event) { |
|
297 | this._compcallback(res); | |
535 | downandpress(event,1) |
|
298 | ||
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(); |
|
|||
545 | }; |
|
299 | }; | |
546 |
|
300 | |||
547 |
|
301 |
@@ -1,5 +1,4 | |||||
1 | {% extends page.html %} |
|
1 | {% extends page.html %} | |
2 |
|
||||
3 | {% block stylesheet %} |
|
2 | {% block stylesheet %} | |
4 |
|
3 | |||
5 | {% if mathjax_url %} |
|
4 | {% if mathjax_url %} | |
@@ -220,6 +219,7 data-notebook-id={{notebook_id}} | |||||
220 | <script src="{{ static_url("js/initmathjax.js") }}" type="text/javascript" charset="utf-8"></script> |
|
219 | <script src="{{ static_url("js/initmathjax.js") }}" type="text/javascript" charset="utf-8"></script> | |
221 | <script src="{{ static_url("js/cell.js") }}" type="text/javascript" charset="utf-8"></script> |
|
220 | <script src="{{ static_url("js/cell.js") }}" type="text/javascript" charset="utf-8"></script> | |
222 | <script src="{{ static_url("js/codecell.js") }}" type="text/javascript" charset="utf-8"></script> |
|
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 | <script src="{{ static_url("js/textcell.js") }}" type="text/javascript" charset="utf-8"></script> |
|
223 | <script src="{{ static_url("js/textcell.js") }}" type="text/javascript" charset="utf-8"></script> | |
224 | <script src="{{ static_url("js/kernel.js") }}" type="text/javascript" charset="utf-8"></script> |
|
224 | <script src="{{ static_url("js/kernel.js") }}" type="text/javascript" charset="utf-8"></script> | |
225 | <script src="{{ static_url("js/savewidget.js") }}" type="text/javascript" charset="utf-8"></script> |
|
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 | <script src="{{ static_url("js/notificationwidget.js") }}" type="text/javascript" charset="utf-8"></script> |
|
231 | <script src="{{ static_url("js/notificationwidget.js") }}" type="text/javascript" charset="utf-8"></script> | |
232 | <script src="{{ static_url("js/notebookmain.js") }}" type="text/javascript" charset="utf-8"></script> |
|
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 | {% end %} |
|
237 | {% end %} | |
235 |
|
238 |
General Comments 0
You need to be logged in to leave comments.
Login now