//---------------------------------------------------------------------------- // Copyright (C) 2008-2011 The IPython Development Team // // Distributed under the terms of the BSD License. The full license is in // the file COPYING, distributed as part of this software. //---------------------------------------------------------------------------- //============================================================================ // CodeCell //============================================================================ var IPython = (function (IPython) { var utils = IPython.utils; var CodeCell = function (notebook) { this.code_mirror = null; this.input_prompt_number = null; this.is_completing = false; this.completion_cursor = null; this.outputs = []; this.collapsed = false; this.tooltip_timeout = null; this.clear_out_timeout = null; IPython.Cell.apply(this, arguments); }; CodeCell.prototype = new IPython.Cell(); CodeCell.prototype.create_element = function () { var cell = $('
').addClass('cell border-box-sizing code_cell vbox'); cell.attr('tabindex','2'); var input = $('
').addClass('input hbox'); input.append($('
').addClass('prompt input_prompt')); var input_area = $('
').addClass('input_area box-flex1'); this.code_mirror = CodeMirror(input_area.get(0), { indentUnit : 4, mode: 'python', theme: 'ipython', readOnly: this.read_only, onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this) }); input.append(input_area); var output = $('
').addClass('output vbox'); cell.append(input).append(output); this.element = cell; this.collapse(); }; //TODO, try to diminish the number of parameters. CodeCell.prototype.request_tooltip_after_time = function (pre_cursor,time){ var that = this; if (pre_cursor === "" || pre_cursor === "(" ) { // don't do anything if line beggin with '(' or is empty } else { // Will set a timer to request tooltip in `time` that.tooltip_timeout = setTimeout(function(){ IPython.notebook.request_tool_tip(that, pre_cursor) },time); } }; CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) { // This method gets called in CodeMirror's onKeyDown/onKeyPress // handlers and is used to provide custom key handling. Its return // value is used to determine if CodeMirror should ignore the event: // true = ignore, false = don't ignore. if (this.read_only){ return false; } // note that we are comparing and setting the time to wait at each key press. // a better wqy might be to generate a new function on each time change and // assign it to CodeCell.prototype.request_tooltip_after_time tooltip_wait_time = this.notebook.time_before_tooltip; tooltip_on_tab = this.notebook.tooltip_on_tab; var that = this; // whatever key is pressed, first, cancel the tooltip request before // they are sent, and remove tooltip if any if(event.type === 'keydown' ) { that.remove_and_cancel_tooltip(); }; if (event.keyCode === 13 && (event.shiftKey || event.ctrlKey)) { // Always ignore shift-enter in CodeMirror as we handle it. return true; } else if (event.which === 40 && event.type === 'keypress' && tooltip_wait_time >= 0) { // triger aon keypress (!) otherwise inconsistent event.which depending on plateform // browser and keyboard layout ! // Pressing '(' , request tooltip, don't forget to reappend it var cursor = editor.getCursor(); var pre_cursor = editor.getRange({line:cursor.line,ch:0},cursor).trim()+'('; that.request_tooltip_after_time(pre_cursor,tooltip_wait_time); } else if (event.which === 38) { // If we are not at the top, let CM handle the up arrow and // prevent the global keydown handler from handling it. if (!that.at_top()) { event.stop(); return false; } else { return true; }; } else if (event.which === 40) { // If we are not at the bottom, let CM handle the down arrow and // prevent the global keydown handler from handling it. if (!that.at_bottom()) { event.stop(); return false; } else { return true; }; } else if (event.keyCode === 9 && event.type == 'keydown') { // Tab completion. var cur = editor.getCursor(); //Do not trim here because of tooltip var pre_cursor = editor.getRange({line:cur.line,ch:0},cur); if (pre_cursor.trim() === "") { // Don't autocomplete if the part of the line before the cursor // is empty. In this case, let CodeMirror handle indentation. return false; } else if ((pre_cursor.substr(-1) === "("|| pre_cursor.substr(-1) === " ") && tooltip_on_tab ) { that.request_tooltip_after_time(pre_cursor,0); // Prevent the event from bubbling up. event.stop(); // 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); return true; }; } else if (event.keyCode === 8 && event.type == 'keydown') { // If backspace and the line ends with 4 spaces, remove them. var cur = editor.getCursor(); var line = editor.getLine(cur.line); var ending = line.slice(-4); if (ending === ' ') { editor.replaceRange('', {line: cur.line, ch: cur.ch-4}, {line: cur.line, ch: cur.ch} ); event.stop(); return true; } else { return false; }; } else { // keypress/keyup also trigger on TAB press, and we don't want to // use those to disable tab completion. if (this.is_completing && event.keyCode !== 9) { var ed_cur = editor.getCursor(); var cc_cur = this.completion_cursor; if (ed_cur.line !== cc_cur.line || ed_cur.ch !== cc_cur.ch) { this.is_completing = false; this.completion_cursor = null; }; }; return false; }; return false; }; CodeCell.prototype.remove_and_cancel_tooltip = function() { // note that we don't handle closing directly inside the calltip // as in the completer, because it is not focusable, so won't // get the event. if (this.tooltip_timeout != null){ clearTimeout(this.tooltip_timeout); $('#tooltip').remove(); this.tooltip_timeout = null; } } CodeCell.prototype.finish_tooltip = function (reply) { // Extract call tip data; the priority is call, init, main. defstring = reply.call_def; if (defstring == null) { defstring = reply.init_definition; } if (defstring == null) { defstring = reply.definition; } docstring = reply.call_docstring; if (docstring == null) { docstring = reply.init_docstring; } if (docstring == null) { docstring = reply.docstring; } if (docstring == null) { docstring = ""; } name=reply.name; var that = this; var tooltip = $('
').attr('id', 'tooltip').addClass('tooltip'); // remove to have the tooltip not Limited in X and Y tooltip.addClass('smalltooltip'); var pre=$('
').html(utils.fixConsole(docstring));
        var expandlink=$('').attr('href',"#");
            expandlink.addClass("ui-corner-all"); //rounded corner
            expandlink.attr('role',"button");
            //expandlink.addClass('ui-button');
            //expandlink.addClass('ui-state-default');
        var expandspan=$('').text('Expand');
            expandspan.addClass('ui-icon');
            expandspan.addClass('ui-icon-plus');
        expandlink.append(expandspan);
        expandlink.attr('id','expanbutton');
        expandlink.click(function(){
            tooltip.removeClass('smalltooltip');
            tooltip.addClass('bigtooltip');
            $('#expanbutton').remove();
            setTimeout(function(){that.code_mirror.focus();}, 50);
        });
        var morelink=$('').attr('href',"#");
            morelink.attr('role',"button");
            morelink.addClass('ui-button');
            //morelink.addClass("ui-corner-all"); //rounded corner
            //morelink.addClass('ui-state-default');
        var morespan=$('').text('Open in Pager');
            morespan.addClass('ui-icon');
            morespan.addClass('ui-icon-arrowstop-l-n');
        morelink.append(morespan);
        morelink.click(function(){
            var msg_id = IPython.notebook.kernel.execute(name+"?");
            IPython.notebook.msg_cell_map[msg_id] = IPython.notebook.get_selected_cell().cell_id;
            that.remove_and_cancel_tooltip();
            setTimeout(function(){that.code_mirror.focus();}, 50);
        });

        var closelink=$('').attr('href',"#");
            closelink.attr('role',"button");
            closelink.addClass('ui-button');
            //closelink.addClass("ui-corner-all"); //rounded corner
            //closelink.adClass('ui-state-default'); // grey background and blue cross
        var closespan=$('').text('Close');
            closespan.addClass('ui-icon');
            closespan.addClass('ui-icon-close');
        closelink.append(closespan);
        closelink.click(function(){
            that.remove_and_cancel_tooltip();
            setTimeout(function(){that.code_mirror.focus();}, 50);
            });
        //construct the tooltip
        tooltip.append(closelink);
        tooltip.append(expandlink);
        tooltip.append(morelink);
        if(defstring){
            defstring_html = $('
').html(utils.fixConsole(defstring));
            tooltip.append(defstring_html);
        }
        tooltip.append(pre);
        var pos = this.code_mirror.cursorCoords();
        tooltip.css('left',pos.x+'px');
        tooltip.css('top',pos.yBot+'px');
        $('body').append(tooltip);

        // issues with cross-closing if multiple tooltip in less than 5sec
        // keep it comented for now
        // setTimeout(that.remove_and_cancel_tooltip, 5000);
    };

    // 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);
        };


        // 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)
        {
            // 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 = $('