From d676ff3b6f9eb07483e424bbd36e53a1f5ef5e62 2014-09-05 18:28:00
From: MinRK <benjaminrk@gmail.com>
Date: 2014-09-05 18:28:00
Subject: [PATCH] make default cell type configurable

- adds IPython.notebook.default_cell_type
- default is 'code' (matching IPython 2.0)
- special heuristic values include:
  - 'selected': default to selected cell (current master behavior)
  - 'above': default to cell above
  - 'below': default to cell below
---

diff --git a/IPython/html/static/notebook/js/notebook.js b/IPython/html/static/notebook/js/notebook.js
index 8aaf8b4..8b837ee 100644
--- a/IPython/html/static/notebook/js/notebook.js
+++ b/IPython/html/static/notebook/js/notebook.js
@@ -53,7 +53,7 @@ define([
         //          base_url : string
         //          notebook_path : string
         //          notebook_name : string
-        this.config = options.config || {};
+        this.config = utils.mergeopt(Notebook, options.config);
         this.base_url = options.base_url;
         this.notebook_path = options.notebook_path;
         this.notebook_name = options.notebook_name;
@@ -63,6 +63,7 @@ define([
         this.tooltip = new tooltip.Tooltip(this.events);
         this.ws_url = options.ws_url;
         this._session_starting = false;
+        this.default_cell_type = this.config.default_cell_type || 'code';
         // default_kernel_name is a temporary measure while we implement proper
         // kernel selection and delayed start. Do not rely on it.
         this.default_kernel_name = 'python';
@@ -134,6 +135,14 @@ define([
         rawcell_celltoolbar.register(this);
         slideshow_celltoolbar.register(this);
     };
+    
+    Notebook.options_default = {
+        // can be any cell type, or the special values of
+        // 'above', 'below', or 'selected' to get the value from another cell.
+        Notebook: {
+            default_cell_type: 'code',
+        }
+    };
 
 
     /**
@@ -835,10 +844,25 @@ define([
     Notebook.prototype.insert_cell_at_index = function(type, index){
 
         var ncells = this.ncells();
-        index = Math.min(index,ncells);
-        index = Math.max(index,0);
+        index = Math.min(index, ncells);
+        index = Math.max(index, 0);
         var cell = null;
-        type = type || this.get_selected_cell().cell_type;
+        type = type || this.default_cell_type;
+        if (type === 'above') {
+            if (index > 0) {
+                type = this.get_cell(index-1).cell_type;
+            } else {
+                type = 'code';
+            }
+        } else if (type === 'below') {
+            if (index < ncells) {
+                type = this.get_cell(index).cell_type;
+            } else {
+                type = 'code';
+            }
+        } else if (type === 'selected') {
+            type = this.get_selected_cell().cell_type;
+        }
 
         if (ncells === 0 || this.is_valid_cell_index(index) || index === ncells) {
             var cell_options = {
diff --git a/IPython/html/tests/notebook/dualmode_cellinsert.js b/IPython/html/tests/notebook/dualmode_cellinsert.js
index e87f895..039babe 100644
--- a/IPython/html/tests/notebook/dualmode_cellinsert.js
+++ b/IPython/html/tests/notebook/dualmode_cellinsert.js
@@ -12,31 +12,66 @@ casper.notebook_test(function () {
     var c = 'print("c")';
     index = this.append_cell(c);
     this.execute_cell_then(index);
-
+    
+    this.thenEvaluate(function() {
+        IPython.notebook.default_cell_type = 'code';
+    });
+    
     this.then(function () {
         // Cell insertion
         this.select_cell(2);
+        this.trigger_keydown('m'); // Make it markdown
         this.trigger_keydown('a'); // Creates one cell
         this.test.assertEquals(this.get_cell_text(2), '', 'a; New cell 2 text is empty');
-        this.test.assertEquals(this.get_cell(2).cell_type, 'code', 'a; inserts a code cell when on code cell');
+        this.test.assertEquals(this.get_cell(2).cell_type, 'code', 'a; inserts a code cell');
         this.validate_notebook_state('a', 'command', 2);
         this.trigger_keydown('b'); // Creates one cell
         this.test.assertEquals(this.get_cell_text(2), '', 'b; Cell 2 text is still empty');
         this.test.assertEquals(this.get_cell_text(3), '', 'b; New cell 3 text is empty');
-        this.test.assertEquals(this.get_cell(3).cell_type, 'code', 'b; inserts a code cell when on code cell');
+        this.test.assertEquals(this.get_cell(3).cell_type, 'code', 'b; inserts a code cell');
         this.validate_notebook_state('b', 'command', 3);
     });
+    
+    this.thenEvaluate(function() {
+        IPython.notebook.default_cell_type = 'selected';
+    });
+    
     this.then(function () {
-        // Cell insertion
         this.select_cell(2);
         this.trigger_keydown('m'); // switch it to markdown for the next test
-        this.trigger_keydown('a'); // Creates one cell
-        this.test.assertEquals(this.get_cell_text(2), '', 'a; New cell 2 text is empty');
-        this.test.assertEquals(this.get_cell(2).cell_type, 'markdown', 'a; inserts a markdown cell when on markdown cell');
-        this.validate_notebook_state('a', 'command', 2);
-        this.trigger_keydown('b'); // Creates one cell
-        this.test.assertEquals(this.get_cell_text(2), '', 'b; Cell 2 text is still empty');
-        this.test.assertEquals(this.get_cell(3).cell_type, 'markdown', 'b; inserts a markdown cell when on markdown cell');
-        this.validate_notebook_state('b', 'command', 3);
+        this.test.assertEquals(this.get_cell(2).cell_type, 'markdown', 'test cell is markdown');
+        this.trigger_keydown('a'); // new cell above
+        this.test.assertEquals(this.get_cell(2).cell_type, 'markdown', 'a; inserts a markdown cell when markdown selected');
+        this.trigger_keydown('b'); // new cell below
+        this.test.assertEquals(this.get_cell(3).cell_type, 'markdown', 'b; inserts a markdown cell when markdown selected');
+    });
+    
+    this.thenEvaluate(function() {
+        IPython.notebook.default_cell_type = 'above';
+    });
+    
+    this.then(function () {
+        this.select_cell(2);
+        this.trigger_keydown('1'); // switch it to heading for the next test
+        this.test.assertEquals(this.get_cell(2).cell_type, 'heading', 'test cell is heading');
+        this.trigger_keydown('b'); // new cell below
+        this.test.assertEquals(this.get_cell(3).cell_type, 'heading', 'b; inserts a heading cell below heading cell');
+        this.trigger_keydown('a'); // new cell above
+        this.test.assertEquals(this.get_cell(3).cell_type, 'heading', 'a; inserts a heading cell below heading cell');
+    });
+    
+    this.thenEvaluate(function() {
+        IPython.notebook.default_cell_type = 'below';
+    });
+    
+    this.then(function () {
+        this.select_cell(2);
+        this.trigger_keydown('r'); // switch it to markdown for the next test
+        this.test.assertEquals(this.get_cell(2).cell_type, 'raw', 'test cell is raw');
+        this.trigger_keydown('a'); // new cell above
+        this.test.assertEquals(this.get_cell(2).cell_type, 'raw', 'a; inserts a raw cell above raw cell');
+        this.trigger_keydown('y'); // switch it to code for the next test
+        this.trigger_keydown('b'); // new cell below
+        this.test.assertEquals(this.get_cell(3).cell_type, 'raw', 'b; inserts a raw cell above raw cell');
     });
 });