diff --git a/IPython/html/nbconvert/tests/test_nbconvert_handlers.py b/IPython/html/nbconvert/tests/test_nbconvert_handlers.py
index 0e3cec7..87ce4ba 100644
--- a/IPython/html/nbconvert/tests/test_nbconvert_handlers.py
+++ b/IPython/html/nbconvert/tests/test_nbconvert_handlers.py
@@ -11,7 +11,7 @@ import requests
from IPython.html.utils import url_path_join
from IPython.html.tests.launchnotebook import NotebookTestBase, assert_http_error
from IPython.nbformat.current import (new_notebook, write,
- new_heading_cell, new_code_cell,
+ new_markdown_cell, new_code_cell,
new_output)
from IPython.testing.decorators import onlyif_cmds_exist
@@ -55,7 +55,7 @@ class APITest(NotebookTestBase):
nb = new_notebook()
- nb.cells.append(new_heading_cell(u'Created by test ³'))
+ nb.cells.append(new_markdown_cell(u'Created by test ³'))
cc1 = new_code_cell(source=u'print(2*6)')
cc1.outputs.append(new_output(output_type="stream", text=u'12'))
cc1.outputs.append(new_output(output_type="execute_result",
diff --git a/IPython/html/services/contents/filemanager.py b/IPython/html/services/contents/filemanager.py
index 8077de1..14ed305 100644
--- a/IPython/html/services/contents/filemanager.py
+++ b/IPython/html/services/contents/filemanager.py
@@ -255,7 +255,7 @@ class FileContentsManager(ContentsManager):
try:
nb = current.read(f, u'json')
except Exception as e:
- raise web.HTTPError(400, u"Unreadable Notebook: %s %s" % (os_path, e))
+ raise web.HTTPError(400, u"Unreadable Notebook: %s %r" % (os_path, e))
self.mark_trusted_cells(nb, name, path)
model['content'] = nb
model['format'] = 'json'
diff --git a/IPython/html/services/contents/tests/test_contents_api.py b/IPython/html/services/contents/tests/test_contents_api.py
index bb5a175..0b704de 100644
--- a/IPython/html/services/contents/tests/test_contents_api.py
+++ b/IPython/html/services/contents/tests/test_contents_api.py
@@ -16,7 +16,7 @@ from IPython.html.utils import url_path_join, url_escape
from IPython.html.tests.launchnotebook import NotebookTestBase, assert_http_error
from IPython.nbformat import current
from IPython.nbformat.current import (new_notebook, write, read,
- new_heading_cell, to_notebook_json)
+ new_markdown_cell, to_notebook_json)
from IPython.nbformat import v2
from IPython.utils import py3compat
from IPython.utils.data import uniq_stable
@@ -415,7 +415,7 @@ class APITest(NotebookTestBase):
resp = self.api.read('a.ipynb', 'foo')
nbcontent = json.loads(resp.text)['content']
nb = to_notebook_json(nbcontent)
- nb.cells.append(new_heading_cell(u'Created by test ³'))
+ nb.cells.append(new_markdown_cell(u'Created by test ³'))
nbmodel= {'name': 'a.ipynb', 'path':'foo', 'content': nb, 'type': 'notebook'}
resp = self.api.save('a.ipynb', path='foo', body=json.dumps(nbmodel))
@@ -452,7 +452,7 @@ class APITest(NotebookTestBase):
# Modify it
nbcontent = json.loads(resp.text)['content']
nb = to_notebook_json(nbcontent)
- hcell = new_heading_cell('Created by test')
+ hcell = new_markdown_cell('Created by test')
nb.cells.append(hcell)
# Save
nbmodel= {'name': 'a.ipynb', 'path':'foo', 'content': nb, 'type': 'notebook'}
diff --git a/IPython/html/static/notebook/js/maintoolbar.js b/IPython/html/static/notebook/js/maintoolbar.js
index 6bb0f1b..becca07 100644
--- a/IPython/html/static/notebook/js/maintoolbar.js
+++ b/IPython/html/static/notebook/js/maintoolbar.js
@@ -139,12 +139,6 @@ define([
.append($('').attr('value','code').text('Code'))
.append($('').attr('value','markdown').text('Markdown'))
.append($('').attr('value','raw').text('Raw NBConvert'))
- .append($('').attr('value','heading1').text('Heading 1'))
- .append($('').attr('value','heading2').text('Heading 2'))
- .append($('').attr('value','heading3').text('Heading 3'))
- .append($('').attr('value','heading4').text('Heading 4'))
- .append($('').attr('value','heading5').text('Heading 5'))
- .append($('').attr('value','heading6').text('Heading 6'))
);
};
@@ -190,24 +184,18 @@ define([
this.element.find('#cell_type').change(function () {
var cell_type = $(this).val();
- if (cell_type === 'code') {
+ switch (cell_type) {
+ case 'code':
that.notebook.to_code();
- } else if (cell_type === 'markdown') {
+ break;
+ case 'markdown':
that.notebook.to_markdown();
- } else if (cell_type === 'raw') {
+ break;
+ case 'raw':
that.notebook.to_raw();
- } else if (cell_type === 'heading1') {
- that.notebook.to_heading(undefined, 1);
- } else if (cell_type === 'heading2') {
- that.notebook.to_heading(undefined, 2);
- } else if (cell_type === 'heading3') {
- that.notebook.to_heading(undefined, 3);
- } else if (cell_type === 'heading4') {
- that.notebook.to_heading(undefined, 4);
- } else if (cell_type === 'heading5') {
- that.notebook.to_heading(undefined, 5);
- } else if (cell_type === 'heading6') {
- that.notebook.to_heading(undefined, 6);
+ break;
+ default:
+ console.log("unrecognized cell type:", cell_type);
}
});
this.events.on('selected_cell_type_changed.Notebook', function (event, data) {
diff --git a/IPython/html/static/notebook/js/notebook.js b/IPython/html/static/notebook/js/notebook.js
index 98c6111..bc75dfc 100644
--- a/IPython/html/static/notebook/js/notebook.js
+++ b/IPython/html/static/notebook/js/notebook.js
@@ -824,7 +824,7 @@ define([
* Index will be brought back into the accessible range [0,n]
*
* @method insert_cell_at_index
- * @param [type] {string} in ['code','markdown','heading'], defaults to 'code'
+ * @param [type] {string} in ['code','markdown', 'raw'], defaults to 'code'
* @param [index] {int} a valid index where to insert cell
*
* @return cell {cell|null} created cell or null
@@ -860,15 +860,19 @@ define([
notebook: this,
tooltip: this.tooltip,
};
- if (type === 'code') {
+ switch(type) {
+ case 'code':
cell = new codecell.CodeCell(this.kernel, cell_options);
cell.set_input_prompt();
- } else if (type === 'markdown') {
+ break;
+ case 'markdown':
cell = new textcell.MarkdownCell(cell_options);
- } else if (type === 'raw') {
+ break;
+ case 'raw':
cell = new textcell.RawCell(cell_options);
- } else if (type === 'heading') {
- cell = new textcell.HeadingCell(cell_options);
+ break;
+ default:
+ console.log("invalid cell type: ", type);
}
if(this._insert_element_at_index(cell.element,index)) {
@@ -1090,10 +1094,10 @@ define([
if (this.is_valid_cell_index(i)) {
var source_cell = this.get_cell(i);
var target_cell = null;
- if (source_cell instanceof textcell.HeadingCell) {
- source_cell.set_level(level);
+ if (source_cell instanceof textcell.MarkdownCell) {
+ source_cell.set_heading_level(level);
} else {
- target_cell = this.insert_cell_below('heading',i);
+ target_cell = this.insert_cell_below('markdown',i);
var text = source_cell.get_text();
if (text === source_cell.placeholder) {
text = '';
@@ -1101,9 +1105,9 @@ define([
//metadata
target_cell.metadata = source_cell.metadata;
// We must show the editor before setting its contents
- target_cell.set_level(level);
target_cell.unrender();
target_cell.set_text(text);
+ target_cell.set_heading_level(level);
// make this value the starting point, so that we can only undo
// to this state, instead of a blank cell
target_cell.code_mirror.clearHistory();
@@ -1117,7 +1121,7 @@ define([
}
this.set_dirty(true);
this.events.trigger('selected_cell_type_changed.Notebook',
- {'cell_type':'heading',level:level}
+ {'cell_type':'markdown',level:level}
);
}
};
@@ -1526,8 +1530,8 @@ define([
}
this.codemirror_mode = newmode;
codecell.CodeCell.options_default.cm_config.mode = newmode;
- modename = newmode.mode || newmode.name || newmode
-
+ modename = newmode.mode || newmode.name || newmode;
+
that = this;
utils.requireCodeMirrorMode(modename, function () {
$.map(that.get_cells(), function(cell, i) {
@@ -1539,7 +1543,7 @@ define([
cell.cm_config.mode = newmode;
}
});
- })
+ });
};
// Session related things
@@ -2383,13 +2387,13 @@ define([
Notebook.prototype.load_notebook_error = function (xhr, status, error) {
this.events.trigger('notebook_load_failed.Notebook', [xhr, status, error]);
utils.log_ajax_error(xhr, status, error);
- var msg;
+ var msg = $("
");
if (xhr.status === 400) {
- msg = escape(utils.ajax_error_msg(xhr));
+ msg.text(utils.ajax_error_msg(xhr));
} else if (xhr.status === 500) {
- msg = "An unknown error occurred while loading this notebook. " +
+ msg.text("An unknown error occurred while loading this notebook. " +
"This version can load notebook formats " +
- "v" + this.nbformat + " or earlier. See the server log for details.";
+ "v" + this.nbformat + " or earlier. See the server log for details.");
}
dialog.modal({
notebook: this,
diff --git a/IPython/html/static/notebook/js/textcell.js b/IPython/html/static/notebook/js/textcell.js
index f1fcf11..61db7af 100644
--- a/IPython/html/static/notebook/js/textcell.js
+++ b/IPython/html/static/notebook/js/textcell.js
@@ -222,6 +222,26 @@ define([
MarkdownCell.prototype = Object.create(TextCell.prototype);
+ MarkdownCell.prototype.set_heading_level = function (level) {
+ // make a markdown cell a heading
+ level = level || 1;
+ var source = this.get_text();
+ // \s\S appears to be the js version of multi-line dot-all
+ var match = source.match(/(#*)\s*([\s\S]*)/);
+ // strip the leading `#` if it's already there
+ if (match) {
+ source = match[2];
+ }
+ // add `#` markdown heading prefix
+ var new_text = new Array(level + 1).join('#') + ' ' + source;
+
+ this.set_text(new_text);
+ this.refresh();
+ if (this.rendered) {
+ this.render();
+ }
+ };
+
/**
* @method render
*/
@@ -238,6 +258,19 @@ define([
html = mathjaxutils.replace_math(html, math);
html = security.sanitize_html(html);
html = $($.parseHTML(html));
+ // add anchors to headings
+ // console.log(html);
+ html.find(":header").addBack(":header").each(function (i, h) {
+ h = $(h);
+ var hash = h.text().replace(/ /g, '-');
+ h.attr('id', hash);
+ h.append(
+ $('
')
+ .addClass('anchor-link')
+ .attr('href', '#' + hash)
+ .text('¶')
+ );
+ })
// links in markdown cells should open in new tabs
html.find("a[href]").not('[href^="#"]').attr("target", "_blank");
this.set_rendered(html);
@@ -305,121 +338,15 @@ define([
return cont;
};
-
- var HeadingCell = function (options) {
- // Constructor
- //
- // Parameters:
- // options: dictionary
- // Dictionary of keyword arguments.
- // events: $(Events) instance
- // config: dictionary
- // keyboard_manager: KeyboardManager instance
- // notebook: Notebook instance
- options = options || {};
- var config = utils.mergeopt(HeadingCell, options.config);
- TextCell.apply(this, [$.extend({}, options, {config: config})]);
-
- this.level = 1;
- this.cell_type = 'heading';
- };
-
- HeadingCell.options_default = {
- cm_config: {
- theme: 'heading-1'
- },
- placeholder: "Type Heading Here"
- };
-
- HeadingCell.prototype = Object.create(TextCell.prototype);
-
- /** @method fromJSON */
- HeadingCell.prototype.fromJSON = function (data) {
- if (data.level !== undefined){
- this.level = data.level;
- }
- TextCell.prototype.fromJSON.apply(this, arguments);
- this.code_mirror.setOption("theme", "heading-"+this.level);
- };
-
-
- /** @method toJSON */
- HeadingCell.prototype.toJSON = function () {
- var data = TextCell.prototype.toJSON.apply(this);
- data.level = this.get_level();
- return data;
- };
-
- /**
- * Change heading level of cell, and re-render
- * @method set_level
- */
- HeadingCell.prototype.set_level = function (level) {
- this.level = level;
- this.code_mirror.setOption("theme", "heading-"+level);
-
- if (this.rendered) {
- this.rendered = false;
- this.render();
- }
- };
-
- /** The depth of header cell, based on html (h1 to h6)
- * @method get_level
- * @return {integer} level - for 1 to 6
- */
- HeadingCell.prototype.get_level = function () {
- return this.level;
- };
-
-
- HeadingCell.prototype.get_rendered = function () {
- var r = this.element.find("div.text_cell_render");
- return r.children().first().html();
- };
-
- HeadingCell.prototype.render = function () {
- var cont = TextCell.prototype.render.apply(this);
- if (cont) {
- var text = this.get_text();
- var math = null;
- // Markdown headings must be a single line
- text = text.replace(/\n/g, ' ');
- if (text === "") { text = this.placeholder; }
- text = new Array(this.level + 1).join("#") + " " + text;
- var text_and_math = mathjaxutils.remove_math(text);
- text = text_and_math[0];
- math = text_and_math[1];
- var html = marked.parser(marked.lexer(text));
- html = mathjaxutils.replace_math(html, math);
- html = security.sanitize_html(html);
- var h = $($.parseHTML(html));
- // add id and linkback anchor
- var hash = h.text().trim().replace(/ /g, '-');
- h.attr('id', hash);
- h.append(
- $('
')
- .addClass('anchor-link')
- .attr('href', '#' + hash)
- .text('¶')
- );
- this.set_rendered(h);
- this.typeset();
- }
- return cont;
- };
-
// Backwards compatability.
IPython.TextCell = TextCell;
IPython.MarkdownCell = MarkdownCell;
IPython.RawCell = RawCell;
- IPython.HeadingCell = HeadingCell;
var textcell = {
- 'TextCell': TextCell,
- 'MarkdownCell': MarkdownCell,
- 'RawCell': RawCell,
- 'HeadingCell': HeadingCell,
+ TextCell: TextCell,
+ MarkdownCell: MarkdownCell,
+ RawCell: RawCell,
};
return textcell;
});
diff --git a/IPython/html/templates/notebook.html b/IPython/html/templates/notebook.html
index 9fc7d97..958374e 100644
--- a/IPython/html/templates/notebook.html
+++ b/IPython/html/templates/notebook.html
@@ -189,12 +189,6 @@ class="notebook_app"
Raw NBConvert
-
Heading 1
-
Heading 2
-
Heading 3
-
Heading 4
-
Heading 5
-
Heading 6
diff --git a/IPython/html/tests/notebook/dualmode_cellinsert.js b/IPython/html/tests/notebook/dualmode_cellinsert.js
index 039babe..0974690 100644
--- a/IPython/html/tests/notebook/dualmode_cellinsert.js
+++ b/IPython/html/tests/notebook/dualmode_cellinsert.js
@@ -52,12 +52,12 @@ casper.notebook_test(function () {
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('y'); // switch it to code for the next test
+ this.test.assertEquals(this.get_cell(2).cell_type, 'code', 'test cell is code');
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.test.assertEquals(this.get_cell(3).cell_type, 'code', 'b; inserts a code cell below code 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.test.assertEquals(this.get_cell(3).cell_type, 'code', 'a; inserts a code cell below code cell');
});
this.thenEvaluate(function() {
diff --git a/IPython/html/tests/notebook/dualmode_cellmode.js b/IPython/html/tests/notebook/dualmode_cellmode.js
index d4bf5f0..3701792 100644
--- a/IPython/html/tests/notebook/dualmode_cellmode.js
+++ b/IPython/html/tests/notebook/dualmode_cellmode.js
@@ -4,25 +4,38 @@
casper.notebook_test(function () {
this.then(function () {
// Cell mode change
- this.select_cell(0);
+ var index = 0;
+ this.select_cell(index);
+ var a = 'hello\nmulti\nline';
+ this.set_cell_text(index, a);
this.trigger_keydown('esc','r');
- this.test.assertEquals(this.get_cell(0).cell_type, 'raw', 'r; cell is raw');
+ this.test.assertEquals(this.get_cell(index).cell_type, 'raw', 'r; cell is raw');
this.trigger_keydown('1');
- this.test.assertEquals(this.get_cell(0).cell_type, 'heading', '1; cell is heading');
- this.test.assertEquals(this.get_cell(0).level, 1, '1; cell is level 1 heading');
+ this.test.assertEquals(this.get_cell(index).cell_type, 'markdown', '1; cell is markdown');
+ this.test.assertEquals(this.get_cell_text(index), '# ' + a, '1; markdown heading');
this.trigger_keydown('2');
- this.test.assertEquals(this.get_cell(0).level, 2, '2; cell is level 2 heading');
+ this.test.assertEquals(this.get_cell(index).cell_type, 'markdown', '2; cell is markdown');
+ this.test.assertEquals(this.get_cell_text(index), '## ' + a, '2; markdown heading');
this.trigger_keydown('3');
- this.test.assertEquals(this.get_cell(0).level, 3, '3; cell is level 3 heading');
+ this.test.assertEquals(this.get_cell(index).cell_type, 'markdown', '3; cell is markdown');
+ this.test.assertEquals(this.get_cell_text(index), '### ' + a, '3; markdown heading');
this.trigger_keydown('4');
- this.test.assertEquals(this.get_cell(0).level, 4, '4; cell is level 4 heading');
+ this.test.assertEquals(this.get_cell(index).cell_type, 'markdown', '4; cell is markdown');
+ this.test.assertEquals(this.get_cell_text(index), '#### ' + a, '4; markdown heading');
this.trigger_keydown('5');
- this.test.assertEquals(this.get_cell(0).level, 5, '5; cell is level 5 heading');
+ this.test.assertEquals(this.get_cell(index).cell_type, 'markdown', '5; cell is markdown');
+ this.test.assertEquals(this.get_cell_text(index), '##### ' + a, '5; markdown heading');
this.trigger_keydown('6');
- this.test.assertEquals(this.get_cell(0).level, 6, '6; cell is level 6 heading');
+ this.test.assertEquals(this.get_cell(index).cell_type, 'markdown', '6; cell is markdown');
+ this.test.assertEquals(this.get_cell_text(index), '###### ' + a, '6; markdown heading');
this.trigger_keydown('m');
- this.test.assertEquals(this.get_cell(0).cell_type, 'markdown', 'm; cell is markdown');
+ this.test.assertEquals(this.get_cell(index).cell_type, 'markdown', 'm; cell is markdown');
+ this.test.assertEquals(this.get_cell_text(index), '###### ' + a, 'm; still markdown heading');
this.trigger_keydown('y');
- this.test.assertEquals(this.get_cell(0).cell_type, 'code', 'y; cell is code');
+ this.test.assertEquals(this.get_cell(index).cell_type, 'code', 'y; cell is code');
+ this.test.assertEquals(this.get_cell_text(index), '###### ' + a, 'y; still has hashes');
+ this.trigger_keydown('1');
+ this.test.assertEquals(this.get_cell(index).cell_type, 'markdown', '1; cell is markdown');
+ this.test.assertEquals(this.get_cell_text(index), '# ' + a, '1; markdown heading');
});
});
\ No newline at end of file
diff --git a/IPython/html/tests/notebook/markdown.js b/IPython/html/tests/notebook/markdown.js
index 5f28d31..4d7897f 100644
--- a/IPython/html/tests/notebook/markdown.js
+++ b/IPython/html/tests/notebook/markdown.js
@@ -10,38 +10,53 @@ casper.notebook_test(function () {
cell.render();
return cell.get_rendered();
});
- this.test.assertEquals(output.trim(), '
Foo
', 'Markdown JS API works.');
+ this.test.assertEquals(output.trim(), '
Foo¶
', 'Markdown JS API works.');
// Test menubar entries.
output = this.evaluate(function () {
$('#to_code').mouseenter().click();
$('#to_markdown').mouseenter().click();
var cell = IPython.notebook.get_selected_cell();
- cell.set_text('# Bar');
+ cell.set_text('**Bar**');
$('#run_cell').mouseenter().click();
return cell.get_rendered();
});
- this.test.assertEquals(output.trim(), '
Bar
', 'Markdown menubar items work.');
+ this.test.assertEquals(output.trim(), '
Bar
', 'Markdown menubar items work.');
// Test toolbar buttons.
output = this.evaluate(function () {
$('#cell_type').val('code').change();
$('#cell_type').val('markdown').change();
var cell = IPython.notebook.get_selected_cell();
- cell.set_text('# Baz');
+ cell.set_text('*Baz*');
$('#run_b').click();
return cell.get_rendered();
});
- this.test.assertEquals(output.trim(), '
Baz
', 'Markdown toolbar items work.');
+ this.test.assertEquals(output.trim(), '
Baz
', 'Markdown toolbar items work.');
- // Test JavaScript models.
- var output = this.evaluate(function () {
+ // Test markdown headings
+ var text = 'multi\nline';
+
+ this.evaluate(function (text) {
var cell = IPython.notebook.insert_cell_at_index('markdown', 0);
- cell.set_text('# Qux');
- cell.render();
- return cell.get_rendered();
- });
- this.test.assertEquals(output.trim(), '
Qux
', 'Markdown JS API works.');
+ cell.set_text(text);
+ }, {text: text});
+
+ var set_level = function (level) {
+ return casper.evaluate(function (level) {
+ var cell = IPython.notebook.get_cell(0);
+ cell.set_heading_level(level);
+ return cell.get_text();
+ }, {level: level});
+ };
+ var level_text;
+ var levels = [ 1, 2, 3, 4, 5, 6, 2, 1 ];
+ for (var idx=0; idx < levels.length; idx++) {
+ var level = levels[idx];
+ level_text = set_level(level);
+ hashes = new Array(level + 1).join('#');
+ this.test.assertEquals(level_text, hashes + ' ' + text, 'markdown set_heading_level ' + level);
+ }
});
diff --git a/IPython/nbconvert/exporters/tests/files/notebook2.ipynb b/IPython/nbconvert/exporters/tests/files/notebook2.ipynb
index aedf783..1c96833 100644
--- a/IPython/nbconvert/exporters/tests/files/notebook2.ipynb
+++ b/IPython/nbconvert/exporters/tests/files/notebook2.ipynb
@@ -1,11 +1,10 @@
{
"cells": [
{
- "cell_type": "heading",
- "level": 1,
+ "cell_type": "markdown",
"metadata": {},
"source": [
- "NumPy and Matplotlib examples"
+ "# NumPy and Matplotlib examples"
]
},
{
diff --git a/IPython/nbconvert/exporters/tests/test_notebook.py b/IPython/nbconvert/exporters/tests/test_notebook.py
index 65e98e4..2af5491 100644
--- a/IPython/nbconvert/exporters/tests/test_notebook.py
+++ b/IPython/nbconvert/exporters/tests/test_notebook.py
@@ -8,6 +8,7 @@ import json
from .base import ExportersTestsBase
from ..notebook import NotebookExporter
+from IPython.nbformat.current import validate
from IPython.testing.tools import assert_big_text_equal
class TestNotebookExporter(ExportersTestsBase):
@@ -29,7 +30,7 @@ class TestNotebookExporter(ExportersTestsBase):
exporter = self.exporter_class(nbformat_version=3)
(output, resources) = exporter.from_filename(self._get_notebook())
nb = json.loads(output)
- self.assertEqual(nb['nbformat'], 3)
+ validate(nb)
def test_downgrade_2(self):
exporter = self.exporter_class(nbformat_version=2)
diff --git a/IPython/nbconvert/filters/markdown.py b/IPython/nbconvert/filters/markdown.py
index 10e57b0..b36f336 100755
--- a/IPython/nbconvert/filters/markdown.py
+++ b/IPython/nbconvert/filters/markdown.py
@@ -21,6 +21,7 @@ from pygments.formatters import HtmlFormatter
from pygments.util import ClassNotFound
# IPython imports
+from IPython.nbconvert.filters.strings import add_anchor
from IPython.nbconvert.utils.pandoc import pandoc
from IPython.nbconvert.utils.exceptions import ConversionException
from IPython.utils.decorators import undoc
@@ -146,6 +147,10 @@ class IPythonRenderer(mistune.Renderer):
formatter = HtmlFormatter()
return highlight(code, lexer, formatter)
+ def header(self, text, level, raw=None):
+ html = super(IPythonRenderer, self).header(text, level, raw=raw)
+ return add_anchor(html)
+
# Pass math through unaltered - mathjax does the rendering in the browser
def block_math(self, text):
return '$$%s$$' % text
diff --git a/IPython/nbconvert/filters/strings.py b/IPython/nbconvert/filters/strings.py
index 8b9a7bd..03c053a 100755
--- a/IPython/nbconvert/filters/strings.py
+++ b/IPython/nbconvert/filters/strings.py
@@ -4,17 +4,9 @@
Contains a collection of useful string manipulation filters for use in Jinja
templates.
"""
-#-----------------------------------------------------------------------------
-# Copyright (c) 2013, the IPython Development Team.
-#
-# Distributed under the terms of the Modified BSD License.
-#
-# The full license is in the file COPYING.txt, distributed with this software.
-#-----------------------------------------------------------------------------
-#-----------------------------------------------------------------------------
-# Imports
-#-----------------------------------------------------------------------------
+# Copyright (c) IPython Development Team.
+# Distributed under the terms of the Modified BSD License.
import os
import re
@@ -28,9 +20,6 @@ from xml.etree import ElementTree
from IPython.core.interactiveshell import InteractiveShell
from IPython.utils import py3compat
-#-----------------------------------------------------------------------------
-# Functions
-#-----------------------------------------------------------------------------
__all__ = [
'wrap_text',
@@ -88,9 +77,9 @@ def html2text(element):
def add_anchor(html):
- """Add an anchor-link to an html header tag
+ """Add an anchor-link to an html header
- For use in heading cells
+ For use on markdown headings
"""
try:
h = ElementTree.fromstring(py3compat.cast_bytes_py2(html, encoding='utf-8'))
diff --git a/IPython/nbconvert/filters/tests/test_markdown.py b/IPython/nbconvert/filters/tests/test_markdown.py
index d2eb698..5da6259 100644
--- a/IPython/nbconvert/filters/tests/test_markdown.py
+++ b/IPython/nbconvert/filters/tests/test_markdown.py
@@ -1,3 +1,4 @@
+# coding: utf-8
"""Tests for conversions from markdown to other formats"""
# Copyright (c) IPython Development Team.
@@ -26,7 +27,8 @@ class TestMarkdown(TestsBase):
'#test',
'##test',
'test\n----',
- 'test [link](https://google.com/)']
+ 'test [link](https://google.com/)',
+ ]
tokens = [
'*test',
@@ -39,7 +41,8 @@ class TestMarkdown(TestsBase):
'test',
'test',
'test',
- ('test', 'https://google.com/')]
+ ('test', 'https://google.com/'),
+ ]
@dec.onlyif_cmds_exist('pandoc')
@@ -87,6 +90,17 @@ class TestMarkdown(TestsBase):
for index, test in enumerate(self.tests):
self._try_markdown(markdown2html, test, self.tokens[index])
+ def test_markdown2html_heading_anchors(self):
+ for md, tokens in [
+ ('# test',
+ ('
test', 'id="test"', u'¶', "anchor-link")
+ ),
+ ('###test head space',
+ ('test head space', 'id="test-head-space"', u'¶', "anchor-link")
+ )
+ ]:
+ self._try_markdown(markdown2html, md, tokens)
+
def test_markdown2html_math(self):
# Mathematical expressions should be passed through unaltered
cases = [("\\begin{equation*}\n"
diff --git a/IPython/nbconvert/templates/html/basic.tpl b/IPython/nbconvert/templates/html/basic.tpl
index ce467ac..40ba790 100644
--- a/IPython/nbconvert/templates/html/basic.tpl
+++ b/IPython/nbconvert/templates/html/basic.tpl
@@ -79,17 +79,6 @@ In [ ]:
{%- endblock markdowncell %}
-{% block headingcell scoped %}
-