From 560f7d15a96930174fc6594a66d244cad1005744 Mon Sep 17 00:00:00 2001 From: Marc Wouts Date: Sun, 3 Nov 2019 19:34:54 +0100 Subject: [PATCH] Multiple pairing in the JupyterLab extension Closes #290 --- HISTORY.rst | 3 +- packages/labextension/CHANGELOG.md | 1 + .../jupyterlab-jupytext-1.0.2.tgz | Bin 3983 -> 0 bytes .../jupyterlab-jupytext-1.1.0.tgz | Bin 0 -> 4916 bytes packages/labextension/package.json | 2 +- packages/labextension/src/index.ts | 208 ++++++++++++------ setup.py | 2 +- 7 files changed, 149 insertions(+), 67 deletions(-) delete mode 100644 packages/labextension/jupyterlab-jupytext-1.0.2.tgz create mode 100644 packages/labextension/jupyterlab-jupytext-1.1.0.tgz diff --git a/HISTORY.rst b/HISTORY.rst index 1a671da22..79684e2ad 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -8,7 +8,8 @@ Release History **Improvements** -- Pairing a notebook to both `.md` and `.py` is now supported in Jupyter. Input cells are loaded from the most recent text representation. (#290). +- Pairing a notebook to both `.md` and `.py` is now supported. Input cells are loaded from the most recent text representation (#290) +- Both the Jupyter Notebook and the Jupyter Lab extension for Jupytext were updated (#290) - Raw cells are now encoded using HTML comments (```` and ````) in Markdown files (#321) - Markdown cells can be delimited with any of ````, ```` or ```` (#344) - Code blocks from Markdown files, when they don't have an explicit language, appear in Markdown cells in Jupyter (#321) diff --git a/packages/labextension/CHANGELOG.md b/packages/labextension/CHANGELOG.md index db8a36534..c5d6f2ee4 100644 --- a/packages/labextension/CHANGELOG.md +++ b/packages/labextension/CHANGELOG.md @@ -1,5 +1,6 @@ # 1.1.0 (2019-11-03) +- Multiple pairings are supported (#290) - The documentation includes the last version numbers for both Jupytext Python and for this extension (#311) - Documentation says clearly that the extension is bundled with the Python package (#350) diff --git a/packages/labextension/jupyterlab-jupytext-1.0.2.tgz b/packages/labextension/jupyterlab-jupytext-1.0.2.tgz deleted file mode 100644 index 275f7370392de3961e746a9f04a8b132d9500b45..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3983 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 diff --git a/packages/labextension/jupyterlab-jupytext-1.1.0.tgz b/packages/labextension/jupyterlab-jupytext-1.1.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..89fc2c04bf03829bf96ee7391774f7e6720c976b GIT binary patch literal 4916 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 literal 0 HcmV?d00001 diff --git a/packages/labextension/package.json b/packages/labextension/package.json index 47c5cedad..ea3f02310 100644 --- a/packages/labextension/package.json +++ b/packages/labextension/package.json @@ -1,6 +1,6 @@ { "name": "jupyterlab-jupytext", - "version": "1.0.2", + "version": "1.1.0", "description": "A JupyterLab extension for Jupytext", "keywords": [ "jupyter", diff --git a/packages/labextension/src/index.ts b/packages/labextension/src/index.ts index 417a37f9b..86b1be848 100644 --- a/packages/labextension/src/index.ts +++ b/packages/labextension/src/index.ts @@ -6,67 +6,112 @@ import { INotebookTracker } from "@jupyterlab/notebook"; import { nbformat } from "@jupyterlab/coreutils"; +interface JupytextRepresentation { + format_name: string; + extension: string; +}; + interface JupytextSection { formats?: string; notebook_metadata_filter?: string; cell_metadata_filter?: string; -} + text_representation?: JupytextRepresentation +}; const JUPYTEXT_FORMATS = [ { - formats: "ipynb,auto:light", + format: "ipynb", + label: "Pair Notebook with ipynb document" + }, + { + format: "auto:light", label: "Pair Notebook with light Script" }, { - formats: "ipynb,auto:percent", + format: "auto:percent", label: "Pair Notebook with percent Script" }, { - formats: "ipynb,auto:hydrogen", + format: "auto:hydrogen", label: "Pair Notebook with Hydrogen Script" }, { - formats: "ipynb,md", + format: "md", label: "Pair Notebook with Markdown" }, { - formats: "ipynb,Rmd", + format: "Rmd", label: "Pair Notebook with R Markdown" }, { - formats: "custom", + format: "custom", label: "Custom pairing" }, { - formats: "none", + format: "none", label: "Unpair Notebook" } ]; -function get_selected_format(notebook_tracker: INotebookTracker): string { - if (!notebook_tracker.currentWidget) return null; +function get_jupytext_formats(notebook_tracker: INotebookTracker): Array { + if (!notebook_tracker.currentWidget) return []; - if ( - !notebook_tracker.currentWidget.context.model.metadata.has( - "jupytext" - ) - ) - return "none"; + if (!notebook_tracker.currentWidget.context.model.metadata.has("jupytext")) + return []; const jupytext: JupytextSection = (notebook_tracker.currentWidget.context.model.metadata.get( - "jupytext" + "jupytext" ) as unknown) as JupytextSection; - if (!jupytext.formats) return "none"; + let formats: Array = jupytext && jupytext.formats ? jupytext.formats.split(',') : []; + return formats.filter(function (fmt) { + return fmt !== ''; + }); +} + +function get_selected_formats(notebook_tracker: INotebookTracker): Array { + if (!notebook_tracker.currentWidget) return []; + + let formats = get_jupytext_formats(notebook_tracker); const lang = notebook_tracker.currentWidget.context.model.metadata.get( - "language_info" + "language_info" ) as nbformat.ILanguageInfoMetadata; - return lang - ? jupytext.formats.replace( - "," + lang.file_extension.substring(1) + ":", - ",auto:" - ) - : jupytext.formats; + if (lang && lang.file_extension) { + const script_ext = lang.file_extension.substring(1); + formats = formats.map(function (fmt) { + if (fmt === script_ext) + return 'auto:light'; + return fmt.replace(script_ext + ':', 'auto:'); + }); + } + + let notebook_extension: string | undefined = notebook_tracker.currentWidget.context.path.split('.').pop(); + if (!notebook_extension) + return formats; + + notebook_extension = ['ipynb', 'md', 'Rmd'].indexOf(notebook_extension) == -1 ? 'auto' : notebook_extension; + for (const i in formats) { + const ext = formats[i].split(':')[0]; + if (ext == notebook_extension) + return formats; + } + + // the notebook extension was not found among the formats + if (['ipynb', 'md', 'Rmd'].indexOf(notebook_extension) != -1) + formats.push(notebook_extension); + else { + let format_name = 'light'; + if (notebook_tracker.currentWidget.context.model.metadata.has("jupytext")) { + const jupytext: JupytextSection = (notebook_tracker.currentWidget.context.model.metadata.get( + "jupytext" + ) as unknown) as JupytextSection; + + if (jupytext && jupytext.text_representation && jupytext.text_representation.format_name) + format_name = jupytext.text_representation.format_name; + } + formats.push('auto:' + format_name); + } + return formats; }; /** @@ -81,62 +126,97 @@ const extension: JupyterFrontEndPlugin = { palette: ICommandPalette, notebook_tracker: INotebookTracker ) => { - console.log("JupyterLab extension jupyterlab-jupytext is activated"); - + console.log("JupyterLab extension jupyterlab-jupytext is activated"); + // Jupytext formats JUPYTEXT_FORMATS.forEach((args, rank) => { - const formats: string = args["formats"]; - const command: string = "jupytext:" + formats; + const format: string = args["format"]; + const command: string = "jupytext:" + format; app.commands.addCommand(command, { label: args["label"], isToggled: () => { if (!notebook_tracker.currentWidget) return false; - const jupytext_formats = get_selected_format(notebook_tracker) - - if (formats == "custom") - return ( - jupytext_formats && - [ - "none", - "ipynb,auto:light", - "ipynb,auto:percent", - "ipynb,auto:hydrogen", - "ipynb,md", - "ipynb,Rmd" - ].indexOf(jupytext_formats) < 0 - ); - - return jupytext_formats == formats; + const jupytext_formats = get_selected_formats(notebook_tracker); + + if (format == "custom"){ + for (const i in jupytext_formats) { + const fmt = jupytext_formats[i]; + if (['ipynb', 'auto:light', 'auto:percent', 'auto:hydrogen', 'md', 'Rmd'].indexOf(fmt)==-1) + return true; + } + return false; + } + return jupytext_formats.indexOf(format)!=-1; }, - isEnabled: () => { + isEnabled: () => { if (!notebook_tracker.currentWidget) return false; - if (formats == "custom" || formats == "none") - return true; - const notebook_extension: string = notebook_tracker.currentWidget.context.path.split('.').pop(); - if (notebook_extension == "ipynb") - return true; - if (notebook_extension == "md") - return formats == "ipynb,md"; - if (notebook_extension == "Rmd") - return formats == "ipynb,Rmd"; - return formats != "ipynb,md" && formats != "ipynb,Rmd"; + + const notebook_extension: string | undefined = notebook_tracker.currentWidget.context.path.split('.').pop(); + if (format===notebook_extension) + return false; + + return true; }, execute: () => { - const jupytext: JupytextSection = (notebook_tracker.currentWidget.context.model.metadata.get( + const jupytext: JupytextSection = (notebook_tracker.currentWidget.context.model.metadata.get( "jupytext" ) as unknown) as JupytextSection; - const jupytext_formats = get_selected_format(notebook_tracker); - const target_format = jupytext_formats === formats ? "none": formats; + let formats: Array = get_selected_formats(notebook_tracker); + + // Toggle the selected format console.log("Jupytext: executing command=" + command); - if (formats == "custom") { + if (format == "custom") { alert( "Please edit the notebook metadata directly if you wish a custom configuration." ); return; } + // Toggle the selected format + let notebook_extension: string = notebook_tracker.currentWidget.context.path.split('.').pop(); + notebook_extension = ['ipynb', 'md', 'Rmd'].indexOf(notebook_extension) == -1 ? 'auto' : notebook_extension; + + // Toggle the selected format + const index = formats.indexOf(format); + if (format === 'none') { + // Only keep one format - one that matches the current extension + for (const i in formats) { + const fmt = formats[i]; + if (fmt.split(':')[0] === notebook_extension) { + formats = [fmt]; + break; + } + } + } else if (index != -1) { + formats.splice(index, 1); + + // The current file extension can't be unpaired + let ext_found = false; + for (const i in formats) { + const fmt = formats[i]; + if (fmt.split(':')[0] === notebook_extension) { + ext_found = true; + break; + } + } + + if (!ext_found) + return; + } else { + // We can't have the same extension multiple times + const new_formats = []; + for (const i in formats) { + const fmt = formats[i]; + if (fmt.split(':')[0] !== format.split(':')[0]) { + new_formats.push(fmt) + } + } + + formats = new_formats; + formats.push(format); + } - if (target_format == "none") { + if (formats.length === 1 && formats[0] === 'ipynb') { if ( !notebook_tracker.currentWidget.context.model.metadata.has( "jupytext" @@ -156,11 +236,11 @@ const extension: JupyterFrontEndPlugin = { } // set the desired format - if (jupytext) jupytext.formats = target_format; + if (jupytext) jupytext.formats = formats.join(); else notebook_tracker.currentWidget.context.model.metadata.set( "jupytext", - { formats: target_format } + { formats: formats.join() } ); } }); @@ -190,7 +270,7 @@ const extension: JupyterFrontEndPlugin = { rank: 1 }); - // Metadata in text representation + // Metadata in text representation app.commands.addCommand("jupytext_metadata", { label: "Include Metadata", isToggled: () => { diff --git a/setup.py b/setup.py index 68e228d0b..ebdb9a111 100644 --- a/setup.py +++ b/setup.py @@ -32,7 +32,7 @@ 'jupytext/nbextension/jupytext_menu.png', 'jupytext/nbextension/jupytext_menu_zoom.png', 'jupytext/nbextension/jupytext.yml']), - ('share/jupyter/lab/extensions', ['packages/labextension/jupyterlab-jupytext-1.0.2.tgz'])], + ('share/jupyter/lab/extensions', ['packages/labextension/jupyterlab-jupytext-1.1.0.tgz'])], entry_points={'console_scripts': ['jupytext = jupytext.cli:jupytext']}, tests_require=['pytest'], install_requires=['nbformat>=4.0.0', 'pyyaml', 'mock;python_version<"3"'],