From 4b889ad5266565f2e3bee0599888663c9003a4d9 2012-05-31 20:08:13 From: Matthias BUSSONNIER Date: 2012-05-31 20:08:13 Subject: [PATCH] implement the completer in a separate class more feature like -completion based on 2 sources : * introspection in kernel * context of current cell (complete with matching words) * each source has its color in the completer --- diff --git a/IPython/frontend/html/notebook/static/css/notebook.css b/IPython/frontend/html/notebook/static/css/notebook.css index 88f9259..b3d4f97 100644 --- a/IPython/frontend/html/notebook/static/css/notebook.css +++ b/IPython/frontend/html/notebook/static/css/notebook.css @@ -273,6 +273,13 @@ div.text_cell_render { font-family: monospace; } +option.context { + background-color: #DEF7FF; +} +option.introspection { + background-color: #EBF4EB; +} + @-moz-keyframes fadeIn { from {opacity:0;} to {opacity:1;} diff --git a/IPython/frontend/html/notebook/static/js/codecell.js b/IPython/frontend/html/notebook/static/js/codecell.js index 5119461..b29b6ed 100644 --- a/IPython/frontend/html/notebook/static/js/codecell.js +++ b/IPython/frontend/html/notebook/static/js/codecell.js @@ -10,7 +10,6 @@ //============================================================================ var IPython = (function (IPython) { - var utils = IPython.utils; var CodeCell = function (notebook) { @@ -23,6 +22,8 @@ var IPython = (function (IPython) { this.tooltip_timeout = null; this.clear_out_timeout = null; IPython.Cell.apply(this, arguments); + var that = this; + this.ccc = new IPython.Completer(function(ed, callback){that.requestCompletion(ed, callback)}); }; @@ -40,8 +41,11 @@ var IPython = (function (IPython) { mode: 'python', theme: 'ipython', readOnly: this.read_only, - onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this) + onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this), }); + var that = this; + ccm = this.code_mirror; + ccc = this.ccc; input.append(input_area); var output = $('
').addClass('output vbox'); cell.append(input).append(output); @@ -129,13 +133,9 @@ var IPython = (function (IPython) { // Prevent CodeMirror from handling the tab. return true; } else { - pre_cursor.trim(); - // Autocomplete the current line. event.stop(); - var line = editor.getLine(cur.line); - this.is_completing = true; - this.completion_cursor = cur; - IPython.notebook.complete_cell(this, line, cur.ch); + this.ccc.startCompletionFor(this.code_mirror); + return true; }; } else if (event.keyCode === 8 && event.type == 'keydown') { @@ -263,285 +263,39 @@ var IPython = (function (IPython) { }; // As you type completer - CodeCell.prototype.finish_completing = function (matched_text, matches) { - if(matched_text[0]=='%'){ - completing_from_magic = true; - completing_to_magic = false; - } else { - completing_from_magic = false; - completing_to_magic = false; - } - //return if not completing or nothing to complete - if (!this.is_completing || matches.length === 0) {return;} - - // for later readability - var key = { tab:9, - esc:27, - backspace:8, - space:32, - shift:16, - enter:13, - // _ is 95 - isCompSymbol : function (code) - { - return (code > 64 && code <= 90) - || (code >= 97 && code <= 122) - || (code == 95) - }, - dismissAndAppend : function (code) - { - chararr = '()[]+-/\\. ,=*'.split(""); - codearr = chararr.map(function(x){return x.charCodeAt(0)}); - return jQuery.inArray(code, codearr) != -1; - } - - } - - // smart completion, sort kwarg ending with '=' - var newm = new Array(); - if(this.notebook.smart_completer) - { - kwargs = new Array(); - other = new Array(); - for(var i = 0 ; i 1 ){ - var tem1, tem2, s, A = A.slice(0).sort(); - tem1 = A[0]; - s = tem1.length; - tem2 = A.pop(); - while(s && tem2.indexOf(tem1) == -1){ - tem1 = tem1.substring(0, --s); - } - shared = tem1; - } - if (shared[0] == '%' && !completing_from_magic) - { - shared = shared.substr(1); - return [shared, true]; - } else { - return [shared, false]; - } - } - - - //try to check if the user is typing tab at least twice after a word - // and completion is "done" - fallback_on_tooltip_after = 2 - if(matches.length == 1 && matched_text === matches[0]) - { - if(this.npressed >fallback_on_tooltip_after && this.prevmatch==matched_text) - { - this.request_tooltip_after_time(matched_text+'(',0); - return; - } - this.prevmatch = matched_text - this.npressed = this.npressed+1; - } - else - { - this.prevmatch = ""; - this.npressed = 0; - } - // end fallback on tooltip - //================================== - // Real completion logic start here - var that = this; - var cur = this.completion_cursor; - var done = false; - - // call to dismmiss the completer - var close = function () { - if (done) return; - done = true; - if (complete != undefined) - {complete.remove();} - that.is_completing = false; - that.completion_cursor = null; - }; - - // update codemirror with the typed text - prev = matched_text - var update = function (inserted_text, event) { - that.code_mirror.replaceRange( - inserted_text, - {line: cur.line, ch: (cur.ch-matched_text.length)}, - {line: cur.line, ch: (cur.ch+prev.length-matched_text.length)} - ); - prev = inserted_text - if(event != null){ - event.stopPropagation(); - event.preventDefault(); - } - }; - // insert the given text and exit the completer - var insert = function (selected_text, event) { - update(selected_text) - close(); - setTimeout(function(){that.code_mirror.focus();}, 50); - }; - - // insert the curent highlited selection and exit - var pick = function () { - insert(select.val()[0],null); - }; - + CodeCell.prototype.requestCompletion= function(ed,callback) + { + this._compcallback = callback; + this._editor = ed; + var cur = ed.getCursor(); + var pre_cursor = this.code_mirror.getRange({line:cur.line,ch:0},cur); + pre_cursor.trim(); + // Autocomplete the current line. + var line = this.code_mirror.getLine(cur.line); + this.is_completing = true; + this.completion_cursor = cur; + IPython.notebook.complete_cell(this, line, cur.ch); + } - // Define function to clear the completer, refill it with the new - // matches, update the pseuso typing field. autopick insert match if - // only one left, in no matches (anymore) dismiss itself by pasting - // what the user have typed until then - var complete_with = function(matches,typed_text,autopick,event) + CodeCell.prototype.finish_completing = function (matched_text, matches) { + // let's build a function that wrap all that stuff into what is needed for the + // new completer: + // + var cur = this._editor.getCursor(); + res = CodeMirror.contextHint(this._editor); + for( i=0; i< matches.length ; i++) { - // If autopick an only one match, past. - // Used to 'pick' when pressing tab - var prefix = ''; - if(completing_to_magic && !completing_from_magic) - { - prefix='%'; - } - if (matches.length < 1) { - insert(prefix+typed_text,event); - if(event != null){ - event.stopPropagation(); - event.preventDefault(); - } - } else if (autopick && matches.length == 1) { - insert(matches[0],event); - if(event != null){ - event.stopPropagation(); - event.preventDefault(); - } - return; - } - //clear the previous completion if any - update(prefix+typed_text,event); - complete.children().children().remove(); - $('#asyoutype').html(""+prefix+matched_text+""+typed_text.substr(matched_text.length)); - select = $('#asyoutypeselect'); - for (var i = 0; i').html(matches[i])); - } - select.children().first().attr('selected','true'); - } - - // create html for completer - var complete = $('
').addClass('completions'); - complete.attr('id','complete'); - complete.append($('

').attr('id', 'asyoutype').html('fixed partuser part'));//pseudo input field - - var select = $('').attr('multiple','true'); + var pos = this.editor.cursorCoords(); + + // TODO: I propose to remove enough horizontal pixel + // to align the text later + this.complete.css('left',pos.x+'px'); + this.complete.css('top',pos.yBot+'px'); + this.complete.append(this.sel); + + $('body').append(this.complete); + //build the container + var that = this; + this.sel.dblclick(function(){that.pick()}); + this.sel.blur(this.close); + this.sel.keydown(function(event){that.keydown(event)}); + + this.build_gui_list(this.raw_result); + var that=this; + //CodeMirror.connect(that.sel, "dblclick", function(){that.pick()}); + + this.sel.focus(); + // Opera sometimes ignores focusing a freshly created node + if (window.opera) setTimeout(function(){if (!this.done) this.sel.focus();}, 100); + // why do we return true ? + return true; + } + + Completer.prototype.insert = function(completion) { + this.editor.replaceRange(completion.str, completion.from, completion.to); + } + + Completer.prototype.build_gui_list = function(completions){ + // Need to clear the all list + for (var i = 0; i < completions.length; ++i) { + var opt = $('