diff --git a/IPython/frontend/html/notebook/static/js/notebook.js b/IPython/frontend/html/notebook/static/js/notebook.js index af6a8ba..ffd090a 100644 --- a/IPython/frontend/html/notebook/static/js/notebook.js +++ b/IPython/frontend/html/notebook/static/js/notebook.js @@ -22,6 +22,9 @@ var IPython = (function (IPython) { this.next_prompt_number = 1; this.kernel = null; this.clipboard = null; + this.undelete_backup = null; + this.undelete_index = null; + this.undelete_below = false; this.paste_enabled = false; this.dirty = false; this.metadata = {}; @@ -257,6 +260,11 @@ var IPython = (function (IPython) { IPython.quick_help.show_keyboard_shortcuts(); that.control_key_active = false; return false; + } else if (event.which === 90 && that.control_key_active) { + // Undo last cell delete = z + that.undelete(); + that.control_key_active = false; + return false; } else if (that.control_key_active) { that.control_key_active = false; return true; @@ -536,13 +544,19 @@ var IPython = (function (IPython) { Notebook.prototype.delete_cell = function (index) { var i = this.index_or_selected(index); + var cell = this.get_selected_cell(); + this.undelete_backup = cell.toJSON(); if (this.is_valid_cell_index(i)) { var ce = this.get_cell_element(i); ce.remove(); if (i === (this.ncells())) { this.select(i-1); + this.undelete_index = i - 1; + this.undelete_below = true; } else { this.select(i); + this.undelete_index = i; + this.undelete_below = false; }; this.dirty = true; }; @@ -560,6 +574,11 @@ var IPython = (function (IPython) { // index = cell index or undefined to insert below selected index = this.index_or_selected(index); var cell = null; + // This is intentionally < rather than <= for the sake of more + // sensible behavior in some cases. + if (this.undelete_index !== null && index < this.undelete_index) { + this.undelete_index = this.undelete_index + 1; + } if (this.ncells() === 0 || this.is_valid_cell_index(index)) { if (type === 'code') { cell = new IPython.CodeCell(this.kernel); @@ -594,6 +613,9 @@ var IPython = (function (IPython) { // index = cell index or undefined to insert above selected index = this.index_or_selected(index); var cell = null; + if (this.undelete_index !== null && index <= this.undelete_index) { + this.undelete_index = this.undelete_index + 1; + } if (this.ncells() === 0 || this.is_valid_cell_index(index)) { if (type === 'code') { cell = new IPython.CodeCell(this.kernel); @@ -818,6 +840,33 @@ var IPython = (function (IPython) { }; }; + // Cell undelete + + Notebook.prototype.undelete = function() { + if (this.undelete_backup !== null && this.undelete_index !== null) { + var current_index = this.get_selected_index(); + if (this.undelete_index < current_index) { + current_index = current_index + 1; + } + if (this.undelete_index >= this.ncells()) { + this.select(this.ncells() - 1); + } + else { + this.select(this.undelete_index); + } + var cell_data = this.undelete_backup; + var new_cell = null; + if (this.undelete_below) { + new_cell = this.insert_cell_below(cell_data.cell_type); + } else { + new_cell = this.insert_cell_above(cell_data.cell_type); + } + new_cell.fromJSON(cell_data); + this.select(current_index); + this.undelete_backup = null; + this.undelete_index = null; + } + } // Split/merge diff --git a/IPython/frontend/html/notebook/static/js/quickhelp.js b/IPython/frontend/html/notebook/static/js/quickhelp.js index e88df2f..5bf0b19 100644 --- a/IPython/frontend/html/notebook/static/js/quickhelp.js +++ b/IPython/frontend/html/notebook/static/js/quickhelp.js @@ -33,6 +33,7 @@ var IPython = (function (IPython) { {key: 'Ctrl-m c', help: 'copy cell'}, {key: 'Ctrl-m v', help: 'paste cell'}, {key: 'Ctrl-m d', help: 'delete cell'}, + {key: 'Ctrl-m z', help: 'undo last cell deletion'}, {key: 'Ctrl-m a', help: 'insert cell above'}, {key: 'Ctrl-m b', help: 'insert cell below'}, {key: 'Ctrl-m o', help: 'toggle output'},