##// END OF EJS Templates
fix replace range bug...
Matthias BUSSONNIER -
Show More
@@ -1,241 +1,241 b''
1 // function completer.
1 // function completer.
2 //
2 //
3 // completer should be a class that take an editor instance, and a list of
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.
4 // function to call to get the list of completion.
5 //
5 //
6 // the function that send back the list of completion should received the
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
7 // editor handle as sole argument, and should return a json object with the
8 // following structure
8 // following structure
9
9
10 // {list: clist, # list of n string containing the completions
10 // {list: clist, # list of n string containing the completions
11 // type : rp, # list of n string containingtype/ origin of the completion
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
12 // # (will be set as the class of the <option> to be able to style
13 // # them according to the origin of the completion)
13 // # them according to the origin of the completion)
14 // from: {line: cur.line, ch: token.start},
14 // from: {line: cur.line, ch: token.start},
15 // to: {line: cur.line, ch: token.end}
15 // to: {line: cur.line, ch: token.end}
16 // };
16 // };
17 //
17 //
18
18
19 var IPython = (function(IPython ) {
19 var IPython = (function(IPython ) {
20 // that will prevent us froom missspelling
20 // that will prevent us froom missspelling
21 "use strict";
21 "use strict";
22
22
23 // easyier key mapping
23 // easyier key mapping
24 var key = { tab:9,
24 var key = { tab:9,
25 esc:27,
25 esc:27,
26 backspace:8,
26 backspace:8,
27 space:32,
27 space:32,
28 shift:16,
28 shift:16,
29 enter:13,
29 enter:13,
30 upArrow:38, // check with keyDown..
30 upArrow:38, // check with keyDown..
31 downArrow :40 // check with keyUp
31 downArrow :40 // check with keyUp
32 };
32 };
33
33
34 // what is the common start of all completions
34 // what is the common start of all completions
35 function sharedStart(B){
35 function sharedStart(B){
36 if(B.length == 1){return B[0]}
36 if(B.length == 1){return B[0]}
37 var A = new Array()
37 var A = new Array()
38 for(var i=0; i< B.length; i++)
38 for(var i=0; i< B.length; i++)
39 {
39 {
40 A.push(B[i].str);
40 A.push(B[i].str);
41 }
41 }
42 if(A.length > 1 ){
42 if(A.length > 1 ){
43 var tem1, tem2, s, A = A.slice(0).sort();
43 var tem1, tem2, s, A = A.slice(0).sort();
44 tem1 = A[0];
44 tem1 = A[0];
45 s = tem1.length;
45 s = tem1.length;
46 tem2 = A.pop();
46 tem2 = A.pop();
47 while(s && tem2.indexOf(tem1) == -1){
47 while(s && tem2.indexOf(tem1) == -1){
48 tem1 = tem1.substring(0, --s);
48 tem1 = tem1.substring(0, --s);
49 }
49 }
50 if (tem1 == "" ){return null;}
50 if (tem1 == "" || tem2.indexOf(tem1) != 0){return null;}
51 return { str : tem1,
51 return { str : tem1,
52 type : "computed",
52 type : "computed",
53 from : B[0].from,
53 from : B[0].from,
54 to : B[0].to
54 to : B[0].to
55 };
55 };
56 }
56 }
57 return null;
57 return null;
58 }
58 }
59
59
60 // user to nsert the given completion
60 // user to nsert the given completion
61 var Completer = function(editor,getHints) {
61 var Completer = function(editor,getHints) {
62 this.editor = editor;
62 this.editor = editor;
63 this.hintfunc = getHints;
63 this.hintfunc = getHints;
64 // if last caractere before cursor is not in this, we stop completing
64 // if last caractere before cursor is not in this, we stop completing
65 this.reg = /[A-Za-z.]/;
65 this.reg = /[A-Za-z.]/;
66 }
66 }
67
67
68 Completer.prototype.startCompletion = function()
68 Completer.prototype.startCompletion = function()
69 {
69 {
70 // call for a 'first' completion, that will set the editor and do some
70 // call for a 'first' completion, that will set the editor and do some
71 // special behaviour like autopicking if only one completion availlable
71 // special behaviour like autopicking if only one completion availlable
72 //
72 //
73 if (this.editor.somethingSelected()) return;
73 if (this.editor.somethingSelected()) return;
74 this.done = false;
74 this.done = false;
75 // use to get focus back on opera
75 // use to get focus back on opera
76 this.carryOnCompletion(true);
76 this.carryOnCompletion(true);
77 }
77 }
78
78
79 Completer.prototype.carryOnCompletion = function(ff)
79 Completer.prototype.carryOnCompletion = function(ff)
80 {
80 {
81 // pass true as parameter if you want the commpleter to autopick
81 // pass true as parameter if you want the commpleter to autopick
82 // when only one completion
82 // when only one completion
83 // as this function is auto;atically reinvoked at each keystroke with
83 // as this function is auto;atically reinvoked at each keystroke with
84 // ff = false
84 // ff = false
85 var cur = this.editor.getCursor();
85 var cur = this.editor.getCursor();
86 var pre_cursor = this.editor.getRange({line:cur.line,ch:cur.ch-1},cur);
86 var pre_cursor = this.editor.getRange({line:cur.line,ch:cur.ch-1},cur);
87
87
88 // we nned to check that we are still on a word boundary
88 // we nned to check that we are still on a word boundary
89 // because while typing the completer is still reinvoking itself
89 // because while typing the completer is still reinvoking itself
90 if(!this.reg.test(pre_cursor)){ this.close(); return;}
90 if(!this.reg.test(pre_cursor)){ this.close(); return;}
91
91
92 this.autopick = false;
92 this.autopick = false;
93 if( ff != 'undefined' && ff==true)
93 if( ff != 'undefined' && ff==true)
94 {
94 {
95 this.autopick=true;
95 this.autopick=true;
96 }
96 }
97 // We want a single cursor position.
97 // We want a single cursor position.
98 if (this.editor.somethingSelected()) return;
98 if (this.editor.somethingSelected()) return;
99
99
100 // there we will need to gather the results for all the function (and merge them ?)
100 // there we will need to gather the results for all the function (and merge them ?)
101 // lets assume for now only one source
101 // lets assume for now only one source
102 //
102 //
103 var that = this;
103 var that = this;
104 this.hintfunc(function(result){that._resume_completion(result)});
104 this.hintfunc(function(result){that._resume_completion(result)});
105 }
105 }
106 Completer.prototype._resume_completion = function(results)
106 Completer.prototype._resume_completion = function(results)
107 {
107 {
108 this.raw_result = results;
108 this.raw_result = results;
109
109
110 // if empty result return
110 // if empty result return
111 if (!this.raw_result || !this.raw_result.length) return;
111 if (!this.raw_result || !this.raw_result.length) return;
112
112
113
113
114
114
115 // When there is only one completion, use it directly.
115 // When there is only one completion, use it directly.
116 if (this.autopick == true && this.raw_result.length == 1)
116 if (this.autopick == true && this.raw_result.length == 1)
117 {
117 {
118 this.insert(this.raw_result[0]);
118 this.insert(this.raw_result[0]);
119 return true;
119 return true;
120 }
120 }
121
121
122 if (this.raw_result.length == 1)
122 if (this.raw_result.length == 1)
123 {
123 {
124 // test if first and only completion totally matches
124 // test if first and only completion totally matches
125 // what is typed, in this case dismiss
125 // what is typed, in this case dismiss
126 var str = this.raw_result[0].str
126 var str = this.raw_result[0].str
127 var cur = this.editor.getCursor();
127 var cur = this.editor.getCursor();
128 var pre_cursor = this.editor.getRange({line:cur.line,ch:cur.ch-str.length},cur);
128 var pre_cursor = this.editor.getRange({line:cur.line,ch:cur.ch-str.length},cur);
129 if(pre_cursor == str){
129 if(pre_cursor == str){
130 this.close();
130 this.close();
131 return ;
131 return ;
132 }
132 }
133 }
133 }
134
134
135 this.complete = $('<div/>').addClass('completions');
135 this.complete = $('<div/>').addClass('completions');
136 this.complete.attr('id','complete');
136 this.complete.attr('id','complete');
137
137
138 this.sel = $('<select/>')
138 this.sel = $('<select/>')
139 .attr('multiple','true')
139 .attr('multiple','true')
140 .attr('size',Math.min(10,this.raw_result.length));
140 .attr('size',Math.min(10,this.raw_result.length));
141 var pos = this.editor.cursorCoords();
141 var pos = this.editor.cursorCoords();
142
142
143 // TODO: I propose to remove enough horizontal pixel
143 // TODO: I propose to remove enough horizontal pixel
144 // to align the text later
144 // to align the text later
145 this.complete.css('left',pos.x+'px');
145 this.complete.css('left',pos.x+'px');
146 this.complete.css('top',pos.yBot+'px');
146 this.complete.css('top',pos.yBot+'px');
147 this.complete.append(this.sel);
147 this.complete.append(this.sel);
148
148
149 $('body').append(this.complete);
149 $('body').append(this.complete);
150 //build the container
150 //build the container
151 var that = this;
151 var that = this;
152 this.sel.dblclick(function(){that.pick()});
152 this.sel.dblclick(function(){that.pick()});
153 this.sel.blur(this.close);
153 this.sel.blur(this.close);
154 this.sel.keydown(function(event){that.keydown(event)});
154 this.sel.keydown(function(event){that.keydown(event)});
155
155
156 this.build_gui_list(this.raw_result);
156 this.build_gui_list(this.raw_result);
157 var that=this;
157 var that=this;
158 //CodeMirror.connect(that.sel, "dblclick", function(){that.pick()});
158 //CodeMirror.connect(that.sel, "dblclick", function(){that.pick()});
159
159
160 this.sel.focus();
160 this.sel.focus();
161 // Opera sometimes ignores focusing a freshly created node
161 // Opera sometimes ignores focusing a freshly created node
162 if (window.opera) setTimeout(function(){if (!this.done) this.sel.focus();}, 100);
162 if (window.opera) setTimeout(function(){if (!this.done) this.sel.focus();}, 100);
163 // why do we return true ?
163 // why do we return true ?
164 return true;
164 return true;
165 }
165 }
166
166
167 Completer.prototype.insert = function(completion) {
167 Completer.prototype.insert = function(completion) {
168 this.editor.replaceRange(completion.str, completion.from, completion.to);
168 this.editor.replaceRange(completion.str, completion.from, completion.to);
169 }
169 }
170
170
171 Completer.prototype.build_gui_list = function(completions){
171 Completer.prototype.build_gui_list = function(completions){
172 // Need to clear the all list
172 // Need to clear the all list
173 for (var i = 0; i < completions.length; ++i) {
173 for (var i = 0; i < completions.length; ++i) {
174 var opt = $('<option/>')
174 var opt = $('<option/>')
175 .text(completions[i].str)
175 .text(completions[i].str)
176 .addClass(completions[i].type);
176 .addClass(completions[i].type);
177 this.sel.append(opt);
177 this.sel.append(opt);
178 }
178 }
179 this.sel.children().first().attr('selected','true');
179 this.sel.children().first().attr('selected','true');
180
180
181 //sel.size = Math.min(10, completions.length);
181 //sel.size = Math.min(10, completions.length);
182 // Hack to hide the scrollbar.
182 // Hack to hide the scrollbar.
183 //if (completions.length <= 10)
183 //if (completions.length <= 10)
184 //this.complete.style.width = (this.sel.clientWidth - 1) + "px";
184 //this.complete.style.width = (this.sel.clientWidth - 1) + "px";
185 }
185 }
186
186
187 Completer.prototype.close = function() {
187 Completer.prototype.close = function() {
188 if (this.done) return;
188 if (this.done) return;
189 this.done = true;
189 this.done = true;
190 $('.completions').remove();
190 $('.completions').remove();
191 }
191 }
192
192
193 Completer.prototype.pick = function(){
193 Completer.prototype.pick = function(){
194 this.insert(this.raw_result[this.sel[0].selectedIndex]);
194 this.insert(this.raw_result[this.sel[0].selectedIndex]);
195 this.close();
195 this.close();
196 var that = this;
196 var that = this;
197 setTimeout(function(){that.editor.focus();}, 50);
197 setTimeout(function(){that.editor.focus();}, 50);
198 }
198 }
199
199
200
200
201 Completer.prototype.keydown = function(event) {
201 Completer.prototype.keydown = function(event) {
202 var code = event.keyCode;
202 var code = event.keyCode;
203 // Enter
203 // Enter
204 if (code == key.enter) {CodeMirror.e_stop(event); this.pick();}
204 if (code == key.enter) {CodeMirror.e_stop(event); this.pick();}
205 // Escape or backspace
205 // Escape or backspace
206 else if (code == key.esc ) {CodeMirror.e_stop(event); this.close(); this.editor.focus();}
206 else if (code == key.esc ) {CodeMirror.e_stop(event); this.close(); this.editor.focus();}
207 else if (code == key.space || code == key.backspace) {this.close(); this.editor.focus();}
207 else if (code == key.space || code == key.backspace) {this.close(); this.editor.focus();}
208 else if (code == key.tab){
208 else if (code == key.tab){
209 //all the fastforwarding operation,
209 //all the fastforwarding operation,
210 //Check that shared start is not null which can append with prefixed completion
210 //Check that shared start is not null which can append with prefixed completion
211 // like %pylab , pylab have no shred start, and ff will result in py<tab><tab>
211 // like %pylab , pylab have no shred start, and ff will result in py<tab><tab>
212 // to erase py
212 // to erase py
213 var sh = sharedStart(this.raw_result)
213 var sh = sharedStart(this.raw_result);
214 if(sh){
214 if(sh){
215 this.insert(sh);
215 this.insert(sh);
216 }
216 }
217 this.close();
217 this.close();
218 CodeMirror.e_stop(event);
218 CodeMirror.e_stop(event);
219 this.editor.focus();
219 this.editor.focus();
220 //reinvoke self
220 //reinvoke self
221 var that = this;
221 var that = this;
222 setTimeout(function(){that.carryOnCompletion();}, 50);
222 setTimeout(function(){that.carryOnCompletion();}, 50);
223 }
223 }
224 else if (code == key.upArrow || code == key.downArrow) {
224 else if (code == key.upArrow || code == key.downArrow) {
225 // need to do that to be able to move the arrow
225 // need to do that to be able to move the arrow
226 // when on the first or last line ofo a code cell
226 // when on the first or last line ofo a code cell
227 event.stopPropagation();
227 event.stopPropagation();
228 }
228 }
229 else if (code != key.upArrow && code != key.downArrow) {
229 else if (code != key.upArrow && code != key.downArrow) {
230 this.close(); this.editor.focus();
230 this.close(); this.editor.focus();
231 //we give focus to the editor immediately and call sell in 50 ms
231 //we give focus to the editor immediately and call sell in 50 ms
232 var that = this;
232 var that = this;
233 setTimeout(function(){that.carryOnCompletion();}, 50);
233 setTimeout(function(){that.carryOnCompletion();}, 50);
234 }
234 }
235 }
235 }
236
236
237
237
238 IPython.Completer = Completer;
238 IPython.Completer = Completer;
239
239
240 return IPython;
240 return IPython;
241 }(IPython));
241 }(IPython));
General Comments 0
You need to be logged in to leave comments. Login now