diff --git a/src/jupyter_contrib_nbextensions/nbextensions/runtools/annotations.odg b/src/jupyter_contrib_nbextensions/nbextensions/runtools/annotations.odg new file mode 100644 index 000000000..ffa9b9b58 Binary files /dev/null and b/src/jupyter_contrib_nbextensions/nbextensions/runtools/annotations.odg differ diff --git a/src/jupyter_contrib_nbextensions/nbextensions/runtools/cellstate.js b/src/jupyter_contrib_nbextensions/nbextensions/runtools/cellstate.js new file mode 100644 index 000000000..71a17c373 --- /dev/null +++ b/src/jupyter_contrib_nbextensions/nbextensions/runtools/cellstate.js @@ -0,0 +1,20 @@ + + + CodeMirror.defineOption("cellstate", false, function(cm, val, old) { + if (old && old != CodeMirror.Init) { + cm.clearGutter(cm.state.cellState.options.gutter); + cm.state.cellState = null; + cm.off("gutterClick", onGutterClick); + cm.off("change", onChange); + cm.off("viewportChange", onViewportChange); + cm.off("swapDoc", onChange); + } + if (val) { + cm.state.cellState = new State(parseOptions(val)); + updateInViewport(cm); + cm.on("gutterClick", onGutterClick); + cm.on("change", onChange); + cm.on("viewportChange", onViewportChange); + cm.on("swapDoc", onChange); + } + }); diff --git a/src/jupyter_contrib_nbextensions/nbextensions/runtools/demo.gif b/src/jupyter_contrib_nbextensions/nbextensions/runtools/demo.gif new file mode 100644 index 000000000..d906670d9 Binary files /dev/null and b/src/jupyter_contrib_nbextensions/nbextensions/runtools/demo.gif differ diff --git a/src/jupyter_contrib_nbextensions/nbextensions/runtools/gutter.css b/src/jupyter_contrib_nbextensions/nbextensions/runtools/gutter.css index e0eee3265..cebc4a998 100644 --- a/src/jupyter_contrib_nbextensions/nbextensions/runtools/gutter.css +++ b/src/jupyter_contrib_nbextensions/nbextensions/runtools/gutter.css @@ -1,5 +1,3 @@ -.CodeMirror-foldgutter { - width: .9em; +.CodeMirror-cellstate { + width: 0.5em; } - - diff --git a/src/jupyter_contrib_nbextensions/nbextensions/runtools/icon.png b/src/jupyter_contrib_nbextensions/nbextensions/runtools/icon.png index dfe59fea3..1c4434056 100644 Binary files a/src/jupyter_contrib_nbextensions/nbextensions/runtools/icon.png and b/src/jupyter_contrib_nbextensions/nbextensions/runtools/icon.png differ diff --git a/src/jupyter_contrib_nbextensions/nbextensions/runtools/main.js b/src/jupyter_contrib_nbextensions/nbextensions/runtools/main.js index 37b78bf60..c021ee546 100644 --- a/src/jupyter_contrib_nbextensions/nbextensions/runtools/main.js +++ b/src/jupyter_contrib_nbextensions/nbextensions/runtools/main.js @@ -1,15 +1,178 @@ -// Add toolbar buttons for extended code execution commands - +// Extended code execution commands and more define([ 'base/js/namespace', 'jquery', 'require', 'base/js/events', - 'codemirror/lib/codemirror' -], function(IPython, $, require, events, codemirror) { + 'services/config', + 'base/js/utils', + 'notebook/js/codecell' +], function(Jupyter, $, require, events, configmod, utils, codecell) { "use strict"; + var base_url = utils.get_body_data("baseUrl"); + var config = new configmod.ConfigSection('notebook', { + base_url: base_url + }); + var run_list = []; /* list of cells to be run */ + + // define default config parameter values + var params = { + run_cells_above: 'Alt-a', + run_cells_below: 'Alt-b', + toggle_marker: 'Alt-t', + mark_all_codecells: 'Alt-m', + unmark_all_codecells: 'Alt-u', + run_marked_cells: 'Alt-r', + run_all_cells: 'Alt-x', + run_all_cells_ignore_errors: 'Alt-f', + stop_execution: 'Ctrl-c', + marked_color: '#20f224', + scheduled_color: '#00def0', + run_color: '#f30a2d' + }; + + // updates default params with any specified in the provided config data + var update_params = function(config_data) { + for (var key in params) { + if (config_data.hasOwnProperty(key)) { + params[key] = config_data[key]; + } + } + }; + + /** + * Add event if user clicks on codemirror gutter + * + */ + function add_gutter_events() { + var ncells = Jupyter.notebook.ncells(); + var cells = Jupyter.notebook.get_cells(); + for (var i = 0; i < ncells; i++) { + var cell = cells[i]; + if ((cell.cell_type === "code")) { + cell.code_mirror.on("gutterClick", changeEvent); + + if (cell.metadata.run_control !== undefined) { + if (cell.metadata.run_control.marked === true) { + var g = cell.code_mirror.getGutterElement(); + $(g).css({ + "background-color": params.marked_color + }); + } + } + } + } + } + + /* + * Initialize toolbar and gutter after config was loaded + */ + function initialize() { + if (Jupyter.notebook.config.data.hasOwnProperty('runtools')) { + update_params(Jupyter.notebook.config.data.runtools) + } + + add_gutter_events(); + + /* Add run control buttons to toolbar */ + Jupyter.toolbar.add_buttons_group([ + Jupyter.actions.register ({ + help: 'Toggle Runtools Toolbar', + icon: 'fa-cogs', + handler: toggle_toolbar + }, 'toggle_runtools') + ]); + $("#toggle_runtools").css({ + 'outline': 'none' + }); + + /* Add keyboard shortcuts */ + var add_command_shortcuts = {}; + add_command_shortcuts[params["run_cells_above"]] = { + help: 'Run cells above', + help_index: 'xa', + handler: function() { + var mode = Jupyter.notebook.get_selected_cell().mode; + Jupyter.notebook.execute_cells_above(); + Jupyter.notebook.select_next(); + var type = Jupyter.notebook.get_selected_cell().cell_type; + if (mode === "edit" && type === "code") Jupyter.notebook.edit_mode(); + return false; + } + }; + add_command_shortcuts[params["run_cells_below"]] = { + help: 'Run cells below', + help_index: 'aa', + handler: function() { + var mode = Jupyter.notebook.get_selected_cell().mode; + Jupyter.notebook.execute_cells_below(); + var type = Jupyter.notebook.get_selected_cell().cell_type; + if (mode === "edit" && type === "code") Jupyter.notebook.edit_mode(); + return false; + } + }; + add_command_shortcuts[params["toggle_marker"]] = { + help: 'Toggle marker', + help_index: 'mt', + handler: function() { + toggle_marker(); + return false; + } + }; + add_command_shortcuts[params["mark_all_codecells"]] = { + help: 'Mark all codecells', + help_index: 'ma', + handler: function() { + mark_all(); + return false; + } + }; + add_command_shortcuts[params["unmark_all_codecells"]] = { + help: 'Unmark all codecells', + help_index: 'mu', + handler: function() { + mark_none(); + return false; + } + }; + add_command_shortcuts[params["run_marked_cells"]] = { + help: 'Run marked cells', + help_index: 'rm', + handler: function() { + run_marked_cells(); + return false; + } + }; + add_command_shortcuts[params["run_all_cells"]] = { + help: 'Run all cells', + help_index: 'ra', + handler: function() { + var pos = Jupyter.notebook.element.scrollTop(); + var ic = Jupyter.notebook.get_selected_index(); + Jupyter.notebook.execute_all_cells(); + Jupyter.notebook.select(ic); + Jupyter.notebook.element.animate({ + scrollTop: pos + }, 100); + return false; + } + }; + add_command_shortcuts[params["run_all_cells_ignore_errors"]] = { + help: 'Run all cells - ignore errors', + help_index: 'rf', + handler: function() { + run_all_cells_ignore_errors(); + return false; + } + }; + Jupyter.keyboard_manager.command_shortcuts.add_shortcuts(add_command_shortcuts); + Jupyter.keyboard_manager.edit_shortcuts.add_shortcuts(add_command_shortcuts); + + events.on('finished_execute.CodeCell', finished_execute_event); + } + /** * Hide or show a cell * @@ -17,9 +180,9 @@ define([ * @param io 'i' for cell input, 'o' for cell output * @param showme {Boolean} show (true) or hide (false) cell */ - function showCell(cell, io, showme) { - if ( io == 'i') { - if ( showme == true) { + function showCell(cell, io, showme) { + if (io === 'i') { + if (showme === true) { cell.element.find("div.input").show(); cell.metadata.hide_input = false; } else { @@ -27,7 +190,7 @@ define([ cell.metadata.hide_input = true; } } else { - if ( showme == true) { + if (showme === true) { cell.element.find('div.output').show(); cell.metadata.hide_output = false; } else { @@ -40,14 +203,14 @@ define([ /** * Hide or show input of all marked code cells * - * @param {Boolean} show show (true) or hide (false) + * @param show {Boolean} show (true) or hide (false) code cells */ function show_input(show) { - var ncells = IPython.notebook.ncells(); - var cells = IPython.notebook.get_cells(); - for (var i=0; i 0) { + var runcell = run_list.shift(); + var end = IPython.notebook.ncells(); + for (var i = 0; i < end; i++) { + if (runcell === IPython.notebook.get_cell(i)) { + if (runcell.metadata.run_control !== undefined && runcell.metadata.run_control.marked === true) { + IPython.notebook.select(i); + var g = runcell.code_mirror.getGutterElement(); + $(g).css({ + "background-color": params.run_color + }); + IPython.notebook.execute_cell(); + return; + } + } + } + } } /** * Run code cells marked in metadata - * + * */ - function run_marked() { - var current = IPython.notebook.get_selected_index(); + function run_marked_cells() { var end = IPython.notebook.ncells(); - for (var i=0; i=0; i--) { - var cells = IPython.notebook.get_cells(); - if ((cells[i].cell_type == "code")) { - if (is_marked(cells[i]) && !is_marked(cells[i + 1])) { - IPython.notebook.move_cell_down(i); - } - } - } - }; - - function makeLockMarker() { + function celltypeMarker(val) { var marker = document.createElement("div"); marker.style.color = "#822"; - marker.innerHTML = ''; + marker.innerHTML = val; return marker; } - - - var lock_cell = function() { - var ncells = IPython.notebook.ncells(); - for (var i=ncells-2; i>=0; i--) { - var cells = IPython.notebook.get_cells(); + /** + * Lock/Unlock current code cell + * if (cell.metadata.run_control != undefined && cell.metadata.run_control.read_only) { + * cell.code_mirror.setOption('readOnly', cell.metadata.run_control.read_only); + */ + var lock_cell = function(locked) { + var ncells = Jupyter.notebook.ncells(); + for (var i = ncells - 2; i >= 0; i--) { + var cells = Jupyter.notebook.get_cells(); if ((cells[i].cell_type === "code") && is_marked(cells[i])) { - cells[i].code_mirror.setOption('readOnly', true); - cells[i].metadata.deletable = false; - cells[i].metadata.locked = true; - cells[i].code_mirror.setGutterMarker(0,"CodeMirror-foldgutter", makeLockMarker()) + if (locked === true) { + cells[i].metadata.editable = false; + set_cell_state(cells[i], 'locked') + } else { + cells[i].metadata.editable = true; + set_cell_state(cells[i], '') + } } } }; - var unlock_cell = function() { - var ncells = IPython.notebook.ncells(); - for (var i=ncells-2; i>=0; i--) { - var cells = IPython.notebook.get_cells(); - if ((cells[i].cell_type === "code" && is_marked(cells[i]))) { - cells[i].code_mirror.setOption('readOnly', false); - cells[i].metadata.deletable = true; - cells[i].metadata.locked = false; - cells[i].code_mirror.setGutterMarker(0,"CodeMirror-foldgutter", null) - } - } - }; - - /** * Execute all cells and don't stop on errors * */ - var execute_all_cells_ignore_errors = function () { - for (var i=0; i < IPython.notebook.ncells(); i++) { - IPython.notebook.select(i); - var cell = IPython.notebook.get_selected_cell(); + var run_all_cells_ignore_errors = function() { + for (var i = 0; i < Jupyter.notebook.ncells(); i++) { + Jupyter.notebook.select(i); + var cell = Jupyter.notebook.get_selected_cell(); cell.execute(false); } }; @@ -290,16 +470,22 @@ define([ * Create floating toolbar * */ - var create_runtools_div = function () { + var create_runtools_div = function() { var btn = '
\
\ \ - \ - \ - \ - \ - \ - \ +
\
\ \ @@ -311,63 +497,179 @@ define([ \ \ \ -
\ -
\ - \ - \ \ \
\
'; var runtools_wrapper = $('
') - .text("Runtools") - .append(btn) - .draggable() - .append("
"); + .text("Runtools") + .append(btn) + .draggable() + .append(""); $("#header").append(runtools_wrapper); - $("#runtools-wrapper").css({'position' : 'absolute'}); - - $('#run_c').on('click', function(e) { IPython.notebook.execute_cell(); e.target.blur() }) - .tooltip({ title : 'Run current cell' , delay: {show: 500, hide: 100}}); - $('#run_ca').on('click', function(e) { IPython.notebook.execute_cells_above(); IPython.notebook.select_next(); e.target.blur() }) - .tooltip({ title : 'Run cells above (Alt-A)' , delay: {show: 500, hide: 100}}); - $('#run_cb').on('click', function(e) { IPython.notebook.execute_cells_below(); e.target.blur() }) - .tooltip({ title : 'Run cells below (Alt-B)' , delay: {show: 500, hide: 100}}); - $('#run_a').on('click', function(e) { IPython.notebook.execute_all_cells(); e.target.blur() }) - .tooltip({ title : 'Run all cells (Alt-X)' , delay: {show: 500, hide: 100}}); - $('#run_af').on('click', function(e) { execute_all_cells_ignore_errors(); e.target.blur() }) - .tooltip({ title : 'Run all - ignore errors' , delay: {show: 500, hide: 100}}); - $('#run_m').on('click', function(e) { run_marked(); e.target.blur() }) - .tooltip({ title : 'Run marked codecells (Alt-R)' , delay: {show: 500, hide: 100}}); - $('#interrupt_b').on('click', function(e) { IPython.notebook.kernel.interrupt(); e.target.blur() }) - .tooltip({ title : 'Interrupt' , delay: {show: 500, hide: 100}}); - - $('#mark_toggle').on('click', function() { toggle_marker() }) - .tooltip({ title : 'Toggle codecell marker (Alt-T)' , delay: {show: 500, hide: 100}}); - $('#mark_all').on('click', function() { mark_all() }) - .tooltip({ title : 'Mark all codecells (Alt-M)' , delay: {show: 500, hide: 100}}); - $('#mark_none').on('click', function() { mark_none() }) - .tooltip({ title : 'Unmark all codecells (Alt-U)' , delay: {show: 500, hide: 100}}); - - $('#show_input').on('click', function() { show_input(true); this.blur() }) - .tooltip({ title : 'Show input area of codecell' , delay: {show: 500, hide: 100}}); - $('#hide_input').on('click', function() { show_input(false); this.blur() }) - .tooltip({ title : 'Hide input area of codecell' , delay: {show: 500, hide: 100}}); - $('#show_output').on('click', function() { show_output(true); this.blur() }) - .tooltip({ title : 'Show output area of codecell' , delay: {show: 500, hide: 100}}); - $('#hide_output').on('click', function() { show_output(false); this.blur() }) - .tooltip({ title : 'Hide output area of codecell' , delay: {show: 500, hide: 100}}); - - $('#up_marked').on('click', function() { move_marked_up(); this.blur() }) - .tooltip({ title : 'Move marked codecells up' , delay: {show: 500, hide: 100}}); - $('#down_marked').on('click', function() { move_marked_down(); this.blur() }) - .tooltip({ title : 'Move marked codecells down' , delay: {show: 500, hide: 100}}); - $('#lock_marked').on('click', function() { lock_cell(); this.blur() }) - .tooltip({ title : 'Lock codecells' , delay: {show: 500, hide: 100}}); - $('#unlock_marked').on('click', function() { unlock_cell(); this.blur() }) - .tooltip({ title : 'Unlock codecells' , delay: {show: 500, hide: 100}}); + $("#runtools-wrapper").css({ + 'position': 'absolute' + }); + $('#run_c').on('click', function(e) { + Jupyter.notebook.execute_cell(); + e.target.blur() + }) + .tooltip({ + delay: { + show: 500, + hide: 100 + } + }); + $('#run_ca').on('click', function(e) { + Jupyter.notebook.execute_cells_above(); + Jupyter.notebook.select_next(); + e.target.blur() + }) + .tooltip({ + delay: { + show: 500, + hide: 100 + } + }); + $('#run_cb').on('click', function(e) { + Jupyter.notebook.execute_cells_below(); + e.target.blur() + }) + .tooltip({ + delay: { + show: 500, + hide: 100 + } + }); + $('#run_a').on('click', function(e) { + Jupyter.notebook.execute_all_cells(); + e.target.blur() + }) + .tooltip({ + delay: { + show: 500, + hide: 100 + } + }); + $('#run_af').on('click', function(e) { + run_all_cells_ignore_errors(); + e.target.blur() + }) + .tooltip({ + delay: { + show: 500, + hide: 100 + } + }); + $('#run_m').on('click', function(e) { + run_marked_cells(); + e.target.blur() + }) + .tooltip({ + delay: { + show: 500, + hide: 100 + } + }); + $('#interrupt_b').on('click', function(e) { + interrupt_execution(); + e.target.blur() + }) + .tooltip({ + delay: { + show: 500, + hide: 100 + } + }); + $('#mark_toggle').on('click', function() { + toggle_marker() + }) + .tooltip({ + delay: { + show: 500, + hide: 100 + } + }); + $('#mark_all').on('click', function() { + mark_all() + }) + .tooltip({ + delay: { + show: 500, + hide: 100 + } + }); + $('#mark_none').on('click', function() { + mark_none() + }) + .tooltip({ + delay: { + show: 500, + hide: 100 + } + }); + $('#show_input').on('click', function() { + show_input(true); + this.blur() + }) + .tooltip({ + delay: { + show: 500, + hide: 100 + } + }); + $('#hide_input').on('click', function() { + show_input(false); + this.blur() + }) + .tooltip({ + delay: { + show: 500, + hide: 100 + } + }); + $('#show_output').on('click', function() { + show_output(true); + this.blur() + }) + .tooltip({ + delay: { + show: 500, + hide: 100 + } + }); + $('#hide_output').on('click', function() { + show_output(false); + this.blur() + }) + .tooltip({ + delay: { + show: 500, + hide: 100 + } + }); + $('#lock_marked').on('click', function() { + lock_cell(true); + this.blur() + }) + .tooltip({ + delay: { + show: 500, + hide: 100 + } + }); + $('#unlock_marked').on('click', function() { + lock_cell(false); + this.blur() + }) + .tooltip({ + delay: { + show: 500, + hide: 100 + } + }); }; /** @@ -389,189 +691,96 @@ define([ create_runtools_div() } }; - - /** - * Add run control buttons to toolbar - * - */ - IPython.toolbar.add_buttons_group([ - { - id : 'toggle_runtools', - label : 'Toggle Runtools Toolbar', - icon : 'fa-cogs', - callback : function () { - toggle_toolbar(); - } - } - ]); - $("#toggle_runtools").css({'outline' : 'none'}); + /** * Add CSS file * * @param name filename */ - var load_css = function (name) { + var load_css = function(name) { var link = document.createElement("link"); link.type = "text/css"; link.rel = "stylesheet"; link.href = require.toUrl(name); document.getElementsByTagName("head")[0].appendChild(link); - }; + }; /** - * Add keyboard shortcuts + * Add gutter to a new cell * - */ - var add_command_shortcuts = { - 'alt-a' : { - help : 'Execute cells above', - help_index : 'xa', - handler : function() { - var mode = IPython.notebook.get_selected_cell().mode; - IPython.notebook.execute_cells_above(); - IPython.notebook.select_next(); - var type = IPython.notebook.get_selected_cell().cell_type; - if (mode == "edit" && type == "code") IPython.notebook.edit_mode(); - return false; - } - }, - 'alt-b' : { - help : 'Execute cells below', - help_index : 'aa', - handler : function() { - var mode = IPython.notebook.get_selected_cell().mode; - IPython.notebook.execute_cells_below(); - var type = IPython.notebook.get_selected_cell().cell_type; - if (mode == "edit" && type == "code") IPython.notebook.edit_mode(); - return false; - } - }, - 'alt-t' : { - help : 'Toggle marker', - help_index : 'mt', - handler : function() { - toggle_marker(); - return false; - } - }, - 'alt-m' : { - help : 'Mark all codecells', - help_index : 'ma', - handler : function() { - mark_all(); - return false; - } - }, - 'alt-u' : { - help : 'Unmark all codecells', - help_index : 'mu', - handler : function() { - mark_none(); - return false; - } - }, - 'alt-r' : { - help : 'Run marked cells', - help_index : 'rm', - handler : function() { - run_marked(); - return false; - } - }, - 'alt-x' : { - help : 'Run all cells', - help_index : 'ra', - handler : function() { - var pos = IPython.notebook.element.scrollTop(); - var ic = IPython.notebook.get_selected_index(); - IPython.notebook.execute_all_cells(); - IPython.notebook.select(ic); - IPython.notebook.element.animate({scrollTop:pos}, 100); - return false; - } - }, - 'alt-f' : { - help : 'Run all cells - ignore errors', - help_index : 'rf', - handler : function() { - execute_all_cells_ignore_errors(); - return false; - } - } - }; - IPython.keyboard_manager.command_shortcuts.add_shortcuts(add_command_shortcuts); - IPython.keyboard_manager.edit_shortcuts.add_shortcuts(add_command_shortcuts); - - /** - * Add event if user clicks on codemirror gutter + * @param event + * @param nbcell * */ - var ncells = IPython.notebook.ncells(); - var cells = IPython.notebook.get_cells(); - for (var i=0; i