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'},