diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a836e97b9..3f6f922f0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -61,7 +61,7 @@ pip install dist/jupytext-XXX.tar.gz ## Jupytext's extension for Jupyter Notebook -Our extension for Jupyter Notebook adds a Jupytext entry to Jupyter Notebook Menu. The code is found at `jupytext/nbextension/index.js`. Instructions to develop that extension are at `jupytext/nbextension/README.md`. +Our extension for Jupyter Notebook adds a Jupytext entry to Jupyter Notebook Menu. The code is found at `jupytext/nbextension/jupytext.js`. Instructions to develop that extension are at `jupytext/nbextension/README.md`. ## Jupytext's extension for JupyterLab diff --git a/jupytext/contentsmanager.py b/jupytext/contentsmanager.py index c86c15dbb..130d50334 100644 --- a/jupytext/contentsmanager.py +++ b/jupytext/contentsmanager.py @@ -308,6 +308,26 @@ def read_one_file(alt_path, alt_fmt): return model + def new_untitled(self, path="", type="", ext=""): + """Create a new untitled file or directory in path + + We override the base function because that one does not take the 'ext' argument + into account when type=="notebook". See https://github.com/mwouts/jupytext/issues/443 + """ + if type != "notebook": + return super(JupytextContentsManager, self).new_untitled(path) + + path = path.strip("/") + if not self.dir_exists(path): + raise HTTPError(404, "No such directory: %s" % path) + + model = {"type": "notebook"} + untitled = self.untitled_notebook + + name = self.increment_filename(untitled + ext, path) + path = u"{0}/{1}".format(path, name) + return self.new(model, path) + def trust_notebook(self, path): """Trust the current notebook""" if path.endswith(".ipynb") or path not in self.paired_notebooks: diff --git a/jupytext/nbextension/README.md b/jupytext/nbextension/README.md index 333f006b2..6cb4a256e 100644 --- a/jupytext/nbextension/README.md +++ b/jupytext/nbextension/README.md @@ -23,8 +23,8 @@ If you wish to develop this extension, install the javascript file locally with: ```bash cd jupytext/nbextension -jupyter nbextension install index.js --symlink --user +jupyter nbextension install jupytext.js --symlink --user jupyter nbextension enable jupytext --user ``` -Then, make the desired changes to `index.js` and reload the extension by simply refreshing the notebook (Ctrl+R). In case your OS does not allow symlinks, edit the copy of `index.js` that is actually used by Jupyter (refer to the output of `jupyter nbextension install --user index.js`). +Then, make the desired changes to `jupytext.js` and reload the extension by simply refreshing the notebook (Ctrl+R). In case your OS does not allow symlinks, edit the copy of `jupytext.js` that is actually used by Jupyter (refer to the output of `jupyter nbextension install --user jupytext.js`). diff --git a/jupytext/nbextension/index.js b/jupytext/nbextension/jupytext.js similarity index 78% rename from jupytext/nbextension/index.js rename to jupytext/nbextension/jupytext.js index 7d5ed871d..d4b7b28cc 100644 --- a/jupytext/nbextension/index.js +++ b/jupytext/nbextension/jupytext.js @@ -8,11 +8,13 @@ define([ 'jquery', 'base/js/namespace', - 'base/js/events' + 'base/js/events', + 'base/js/utils', ], function ( $, Jupyter, - events + events, + utils ) { "use strict"; @@ -231,6 +233,74 @@ define([ ); } + function new_text_notebook(ext='.md') { + // Differences with KernelSelector.prototype.new_notebook from + // https://github.com/jupyter/notebook/blob/ + // 6e9256b0641a85baf664e846515085bac2c082a3/notebook/static/notebook/js/kernelselector.js + // - added ext argument + // - kernel_name is the currently selected one + // - "that" replaced with Jupyter + + var w = window.open('', IPython._target); + // Create a new notebook in the same path as the current + // notebook's path. + var parent = utils.url_path_split(Jupyter.notebook.notebook_path)[0]; + Jupyter.notebook.contents.new_untitled(parent, {type: "notebook", ext:ext}).then( + function (data) { + var url = utils.url_path_join( + Jupyter.notebook.base_url, 'notebooks', + utils.encode_uri_components(data.path) + ); + url += "?kernel_name=" + Jupyter.notebook.metadata.kernelspec.name; + w.location = url; + }, + function(error) { + w.close(); + dialog.modal({ + title : i18n.msg._('Creating Notebook Failed'), + body : i18n.msg.sprintf(i18n.msg._("The error was: %s"), error.message), + buttons : {'OK' : {'class' : 'btn-primary'}} + }); + } + ); + }; + + function text_notebook_entry(type, title, ext=null) { + return $('
') + .append($('') + .attr('id', 'new_notebook_' + type) + .text(title) + .attr('title', title) + .data('ext', ext) + .css('width', '280px') + .attr('href', '#') + .on('click', onClickedTextNotebook) + .prepend($('').addClass('fa menu-icon pull-right')) + ); + }; + + function onClickedTextNotebook(data) { + const ext = $(this).data('ext'); + if (ext) { new_text_notebook(ext); }; + }; + + function updateNewScriptNotebookMenu() { + if (Jupyter.notebook.metadata.language_info) { + var script_ext = Jupyter.notebook.metadata.language_info.file_extension; + var language_name = Jupyter.notebook.metadata.language_info.name; + language_name = language_name.charAt(0).toUpperCase() + language_name.slice(1); + var title = language_name + ' script'; + $('#new_notebook_script').parent().removeClass('disabled'); + $('#new_notebook_script').data('ext', script_ext); + $('#new_notebook_script').attr('title', title); + $('#new_notebook_script').text(title); + } + else { + $('#new_notebook_script').parent().addClass('disabled'); + $('#new_notebook_script').data('ext', null); + }; + }; + var jupytext_menu = function () { if ($('#jupytext_menu').length === 0) { @@ -308,6 +378,24 @@ define([ checkSelectedJupytextFormats(); checkAutosave(); + + // New Text Notebook menu + var NewTextNotebook = $('').attr('href', '#') + .addClass('dropdown-toogle') + .attr('data-toggle', 'dropdown') + .attr('aria-expanded', 'false') + .text('New Text Notebook') + .attr('title', 'New Notebook saved as a text file') + .on('mouseover', updateNewScriptNotebookMenu); + + var TextNotebooks = $('