├── .eslintignore ├── .eslintrc.js ├── .github └── workflows │ ├── build.yml │ └── check-release.yml ├── .gitignore ├── .prettierignore ├── .prettierrc ├── CHANGELOG.md ├── LICENSE ├── MANIFEST.in ├── README.md ├── binder ├── environment.yml ├── example.ipynb └── postBuild ├── install.json ├── jupyter-boilerplate-converter ├── README.md ├── convert.js ├── package-lock.json └── package.json ├── jupyter-config ├── jupyter_notebook_config.d │ └── jupyterlab_snippets.json └── jupyter_server_config.d │ └── jupyterlab_snippets.json ├── jupyterlab_snippets ├── __init__.py ├── _version.py ├── handlers.py └── loader.py ├── package.json ├── pyproject.toml ├── setup.py ├── src ├── index.ts └── snippets.ts ├── style ├── base.css ├── index.css └── index.js └── tsconfig.json /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | coverage 4 | **/*.d.ts 5 | tests 6 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [ 3 | 'eslint:recommended', 4 | 'plugin:@typescript-eslint/eslint-recommended', 5 | 'plugin:@typescript-eslint/recommended', 6 | 'plugin:prettier/recommended' 7 | ], 8 | parser: '@typescript-eslint/parser', 9 | parserOptions: { 10 | project: 'tsconfig.json', 11 | sourceType: 'module' 12 | }, 13 | plugins: ['@typescript-eslint'], 14 | rules: { 15 | '@typescript-eslint/naming-convention': [ 16 | 'error', 17 | { 18 | 'selector': 'interface', 19 | 'format': ['PascalCase'], 20 | 'custom': { 21 | 'regex': '^I[A-Z]', 22 | 'match': true 23 | } 24 | } 25 | ], 26 | '@typescript-eslint/no-unused-vars': ['warn', { args: 'none' }], 27 | '@typescript-eslint/no-explicit-any': 'off', 28 | '@typescript-eslint/no-namespace': 'off', 29 | '@typescript-eslint/no-use-before-define': 'off', 30 | '@typescript-eslint/quotes': [ 31 | 'error', 32 | 'single', 33 | { avoidEscape: true, allowTemplateLiterals: false } 34 | ], 35 | curly: ['error', 'all'], 36 | eqeqeq: 'error', 37 | 'prefer-arrow-callback': 'error' 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: master 6 | pull_request: 7 | branches: '*' 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v2 15 | 16 | - name: Install node 17 | uses: actions/setup-node@v1 18 | with: 19 | node-version: '14.x' 20 | 21 | - name: Install Python 22 | uses: actions/setup-python@v2 23 | with: 24 | python-version: '3.9' 25 | architecture: 'x64' 26 | 27 | - name: Setup pip cache 28 | uses: actions/cache@v2 29 | with: 30 | path: ~/.cache/pip 31 | key: pip-3.7-${{ hashFiles('package.json') }} 32 | restore-keys: | 33 | pip-3.7- 34 | pip- 35 | 36 | - name: Get yarn cache directory path 37 | id: yarn-cache-dir-path 38 | run: echo "::set-output name=dir::$(yarn cache dir)" 39 | 40 | - name: Setup yarn cache 41 | uses: actions/cache@v2 42 | id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) 43 | with: 44 | path: ${{ steps.yarn-cache-dir-path.outputs.dir }} 45 | key: yarn-${{ hashFiles('**/yarn.lock') }} 46 | restore-keys: | 47 | yarn- 48 | 49 | - name: Install dependencies 50 | run: python -m pip install -U jupyterlab~=3.0 jupyter-packaging~=0.10 51 | 52 | - name: Build the extension 53 | run: | 54 | jlpm 55 | jlpm run eslint:check 56 | python -m pip install . 57 | 58 | jupyter server extension list 2>&1 | grep -ie "jupyterlab_snippets.*OK" 59 | jupyter labextension list 2>&1 | grep -ie "jupyterlab-snippets.*OK" 60 | 61 | python -m jupyterlab.browser_check 62 | -------------------------------------------------------------------------------- /.github/workflows/check-release.yml: -------------------------------------------------------------------------------- 1 | name: Check Release 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | branches: 8 | - main 9 | 10 | permissions: 11 | contents: write 12 | 13 | jobs: 14 | check_release: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v2 19 | 20 | - name: Install Python 21 | uses: actions/setup-python@v2 22 | with: 23 | python-version: 3.9 24 | architecture: 'x64' 25 | 26 | - name: Install node 27 | uses: actions/setup-node@v2 28 | with: 29 | node-version: '14.x' 30 | 31 | - name: Get pip cache dir 32 | id: pip-cache 33 | run: | 34 | echo "::set-output name=dir::$(pip cache dir)" 35 | 36 | - name: Cache pip 37 | uses: actions/cache@v1 38 | with: 39 | path: ${{ steps.pip-cache.outputs.dir }} 40 | key: ${{ runner.os }}-pip-${{ hashFiles('setup.py') }} 41 | restore-keys: | 42 | ${{ runner.os }}-pip- 43 | 44 | - name: Cache checked links 45 | uses: actions/cache@v2 46 | with: 47 | path: ~/.cache/pytest-link-check 48 | key: ${{ runner.os }}-linkcheck-${{ hashFiles('**/.md') }}-md-links 49 | restore-keys: | 50 | ${{ runner.os }}-linkcheck- 51 | 52 | - name: Upgrade packaging dependencies 53 | run: | 54 | pip install --upgrade pip setuptools wheel jupyter-packaging~=0.10 --user 55 | 56 | - name: Install Dependencies 57 | run: | 58 | pip install . 59 | 60 | - name: Check Release 61 | uses: jupyter-server/jupyter_releaser/.github/actions/check-release@v1 62 | with: 63 | token: ${{ secrets.GITHUB_TOKEN }} 64 | 65 | - name: Upload Distributions 66 | uses: actions/upload-artifact@v2 67 | with: 68 | name: jupyterlab-snippets-releaser-dist-${{ github.run_number }} 69 | path: .jupyter_releaser_checkout/dist -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.bundle.* 2 | lib/ 3 | node_modules/ 4 | *.egg-info/ 5 | .ipynb_checkpoints 6 | *.tsbuildinfo 7 | jupyterlab_snippets/labextension 8 | 9 | # Created by https://www.gitignore.io/api/python 10 | # Edit at https://www.gitignore.io/?templates=python 11 | 12 | ### Python ### 13 | # Byte-compiled / optimized / DLL files 14 | __pycache__/ 15 | *.py[cod] 16 | *$py.class 17 | 18 | # C extensions 19 | *.so 20 | 21 | # Distribution / packaging 22 | .Python 23 | build/ 24 | develop-eggs/ 25 | dist/ 26 | downloads/ 27 | eggs/ 28 | .eggs/ 29 | lib/ 30 | lib64/ 31 | parts/ 32 | sdist/ 33 | var/ 34 | wheels/ 35 | pip-wheel-metadata/ 36 | share/python-wheels/ 37 | .installed.cfg 38 | *.egg 39 | MANIFEST 40 | 41 | # PyInstaller 42 | # Usually these files are written by a python script from a template 43 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 44 | *.manifest 45 | *.spec 46 | 47 | # Installer logs 48 | pip-log.txt 49 | pip-delete-this-directory.txt 50 | 51 | # Unit test / coverage reports 52 | htmlcov/ 53 | .tox/ 54 | .nox/ 55 | .coverage 56 | .coverage.* 57 | .cache 58 | nosetests.xml 59 | coverage.xml 60 | *.cover 61 | .hypothesis/ 62 | .pytest_cache/ 63 | 64 | # Translations 65 | *.mo 66 | *.pot 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # pyenv 78 | .python-version 79 | 80 | # celery beat schedule file 81 | celerybeat-schedule 82 | 83 | # SageMath parsed files 84 | *.sage.py 85 | 86 | # Spyder project settings 87 | .spyderproject 88 | .spyproject 89 | 90 | # Rope project settings 91 | .ropeproject 92 | 93 | # Mr Developer 94 | .mr.developer.cfg 95 | .project 96 | .pydevproject 97 | 98 | # mkdocs documentation 99 | /site 100 | 101 | # mypy 102 | .mypy_cache/ 103 | .dmypy.json 104 | dmypy.json 105 | 106 | # Pyre type checker 107 | .pyre/ 108 | 109 | # End of https://www.gitignore.io/api/python 110 | 111 | # OSX files 112 | .DS_Store 113 | 114 | yarn.lock 115 | .vscode 116 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | **/node_modules 3 | **/lib 4 | **/package.json 5 | jupyterlab-snippets 6 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "none", 4 | "arrowParens": "avoid" 5 | } 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | 4 | 5 | ## 0.4.1 6 | 7 | ([Full Changelog](https://github.com/QuantStack/jupyterlab-snippets/compare/v0.4.0...928cb72793a42d5f5ae3bed4de103c4640d9fe59)) 8 | 9 | ### Maintenance and upkeep improvements 10 | 11 | - Update to Jupyter Packaging 10, adopt the releaser [#38](https://github.com/QuantStack/jupyterlab-snippets/pull/38) ([@jtpio](https://github.com/jtpio)) 12 | 13 | ### Contributors to this release 14 | 15 | ([GitHub contributors page for this release](https://github.com/QuantStack/jupyterlab-snippets/graphs/contributors?from=2021-03-11&to=2021-09-30&type=c)) 16 | 17 | [@jtpio](https://github.com/search?q=repo%3AQuantStack%2Fjupyterlab-snippets+involves%3Ajtpio+updated%3A2021-03-11..2021-09-30&type=Issues) 18 | 19 | 20 | 21 | ## 0.4.0 22 | 23 | ### Changes 24 | 25 | - Don't list hidden folder #25 26 | - Correct hidden folder removal #26 27 | - Port to JupyterLab 3 #33 28 | 29 | Many thanks @fcollonval for the contributions! 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2020, QuantStack 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE 2 | include *.md 3 | include pyproject.toml 4 | include jupyter-config/jupyterlab-snippets.json 5 | 6 | include package.json 7 | include install.json 8 | include ts*.json 9 | include yarn.lock 10 | 11 | graft jupyterlab_snippets/labextension 12 | 13 | # Javascript files 14 | graft src 15 | graft style 16 | prune **/node_modules 17 | prune lib 18 | 19 | # Patterns to exclude from any directory 20 | global-exclude *~ 21 | global-exclude *.pyc 22 | global-exclude *.pyo 23 | global-exclude .git 24 | global-exclude .ipynb_checkpoints 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jupyterlab-snippets 2 | 3 | ![Github Actions Status](https://github.com/QuantStack/jupyterlab-snippets/workflows/Build/badge.svg) 4 | [![Version](https://img.shields.io/pypi/v/jupyterlab-snippets.svg)](https://pypi.python.org/project/jupyterlab-snippets) 5 | [![Conda Version](https://img.shields.io/conda/vn/conda-forge/jupyterlab-snippets.svg)](https://anaconda.org/conda-forge/jupyterlab-snippets) 6 | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/QuantStack/jupyterlab-snippets/stable?urlpath=lab/tree/binder/example.ipynb) 7 | 8 | Snippets Extension for JupyterLab. 9 | 10 | This extension is composed of a Python package named `jupyterlab-snippets` 11 | for the server extension and a NPM package named `jupyterlab-snippets` 12 | for the frontend extension. 13 | 14 | ## Requirements 15 | 16 | - JupyterLab >= 2.0 17 | - Node.js 18 | 19 | ## Install 20 | 21 | Using conda: 22 | 23 | ``` 24 | conda install -c conda-forge jupyterlab-snippets 25 | ``` 26 | 27 | Using pip: 28 | 29 | ```bash 30 | pip install jupyterlab-snippets 31 | ``` 32 | 33 | If you use JupyterLab 2.x, you will have to rebuild JupyterLab with: 34 | 35 | ```bash 36 | jupyter lab build 37 | ``` 38 | 39 | ## Usage 40 | 41 | Add snippets in `[jupyter_data_dir]/snippets` (see: https://jupyter.readthedocs.io/en/latest/projects/jupyter-directories.html#id2) 42 | 43 | To find the Jupyter data directory, run: 44 | ```bash 45 | $ jupyter --path 46 | ``` 47 | This will for example show the following list on macOS: 48 | ``` 49 | config: 50 | /Users//.jupyter 51 | ... 52 | data: 53 | /Users//Library/Jupyter 54 | /Users//miniconda3/envs//share/jupyter 55 | ... 56 | runtime: 57 | ... 58 | ``` 59 | 60 | Use the first directory listed under `data:` to add files to, these become snippets in the menu. Each file contains one 61 | snippet. Directories can be used to organize the snippet files and will become submenu's in the menu. 62 | 63 | In JupyterLab, use the "Snippets" menu to select the snippet: 64 | 65 | Schermafbeelding 2020-03-30 om 17 25 31 66 | 67 | ## Convert snippets from jupyter-boilerplate format 68 | 69 | See [jupyter-boilerplate-converter](jupyter-boilerplate-converter/README.md) on how to convert snippets from the 70 | [jupyter-boilerplate](https://github.com/moble/jupyter_boilerplate) classic notebook extension (not available for 71 | JupyterLab) to jupyterlab-snippets. 72 | 73 | ## Troubleshoot 74 | 75 | If you are seeing the frontend extension but it is not working, check 76 | that the server extension is enabled: 77 | 78 | ```bash 79 | jupyter serverextension list 80 | ``` 81 | 82 | If the server extension is installed and enabled but you are not seeing 83 | the frontend, check the frontend extension is installed and enabled: 84 | 85 | ```bash 86 | jupyter labextension list 87 | ``` 88 | 89 | If it is installed, try: 90 | 91 | ```bash 92 | jupyter lab clean 93 | jupyter lab build 94 | ``` 95 | 96 | ## Development Install 97 | 98 | Note: You will need NodeJS to build the extension package. 99 | 100 | The `jlpm` command is JupyterLab's pinned version of 101 | [yarn](https://yarnpkg.com/) that is installed with JupyterLab. You may use 102 | `yarn` or `npm` in lieu of `jlpm` below. 103 | 104 | ```bash 105 | # Clone the repo to your local environment 106 | # Change directory to the jupyterlab-snippets directory 107 | # Install package in development mode 108 | pip install -e . 109 | # Link your development version of the extension with JupyterLab 110 | jupyter labextension develop . --overwrite 111 | # Rebuild extension Typescript source after making changes 112 | jlpm run build 113 | ``` 114 | 115 | You can watch the source directory and run JupyterLab at the same time in different terminals to watch for changes in the extension's source and automatically rebuild the extension. 116 | 117 | ```bash 118 | # Watch the source directory in one terminal, automatically rebuilding when needed 119 | jlpm run watch 120 | # Run JupyterLab in another terminal 121 | jupyter lab 122 | ``` 123 | 124 | With the watch command running, every saved change will immediately be built locally and available in your running JupyterLab. Refresh JupyterLab to load the change in your browser (you may need to wait several seconds for the extension to be rebuilt). 125 | 126 | By default, the `jlpm run build` command generates the source maps for this extension to make it easier to debug using the browser dev tools. To also generate source maps for the JupyterLab core extensions, you can run the following command: 127 | 128 | ```bash 129 | jupyter lab build --minimize=False 130 | ``` 131 | 132 | ### Uninstall 133 | 134 | ```bash 135 | pip uninstall jupyterlab-snippets 136 | ``` 137 | -------------------------------------------------------------------------------- /binder/environment.yml: -------------------------------------------------------------------------------- 1 | # a mybinder.org-ready environment for demoing jupyterlab-snippets 2 | # this environment may also be used locally on Linux/MacOS/Windows, e.g. 3 | # 4 | # conda env update --file binder/environment.yml 5 | # conda activate jupyterlab-snippets-demo 6 | # 7 | name: jupyterlab-snippets-demo 8 | 9 | channels: 10 | - conda-forge 11 | 12 | dependencies: 13 | # runtime dependencies 14 | - python >=3.8,<3.9.0a0 15 | - jupyterlab >=3,<4.0.0a0 16 | # labextension build dependencies 17 | - nodejs >=14,<15 18 | - pip 19 | - wheel -------------------------------------------------------------------------------- /binder/example.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "Use the `Snippets` menu option to add snippets to this notebook.\n", 8 | "\n", 9 | "Snippets can be added or edited in the `snippets_symlink` directory.\n", 10 | "\n", 11 | "See https://github.com/QuantStack/jupyterlab-snippets for more information." 12 | ] 13 | }, 14 | { 15 | "cell_type": "code", 16 | "execution_count": null, 17 | "metadata": {}, 18 | "outputs": [], 19 | "source": [] 20 | } 21 | ], 22 | "metadata": { 23 | "kernelspec": { 24 | "display_name": "Python 3", 25 | "language": "python", 26 | "name": "python3" 27 | }, 28 | "language_info": { 29 | "codemirror_mode": { 30 | "name": "ipython", 31 | "version": 3 32 | }, 33 | "file_extension": ".py", 34 | "mimetype": "text/x-python", 35 | "name": "python", 36 | "nbconvert_exporter": "python", 37 | "pygments_lexer": "ipython3", 38 | "version": "3.7.6" 39 | } 40 | }, 41 | "nbformat": 4, 42 | "nbformat_minor": 4 43 | } 44 | -------------------------------------------------------------------------------- /binder/postBuild: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | """ perform a development install of jupyterlab-snippets 3 | On Binder, this will run _after_ the environment has been fully created from 4 | the environment.yml in this directory. 5 | """ 6 | 7 | cd jupyter-boilerplate-converter 8 | git clone git://github.com/moble/jupyter_boilerplate 9 | npm install 10 | 11 | export SNIPPET_DIR=`python -c "from jupyter_core.paths import jupyter_path;print(jupyter_path('snippets')[0])"` 12 | 13 | mkdir -p $SNIPPET_DIR 14 | 15 | npm run convert ./jupyter_boilerplate/snippets_submenus_python/astropy.js $SNIPPET_DIR .py 16 | npm run convert ./jupyter_boilerplate/snippets_submenus_python/matplotlib.js $SNIPPET_DIR .py 17 | npm run convert ./jupyter_boilerplate/snippets_submenus_python/numpy.js $SNIPPET_DIR .py 18 | npm run convert ./jupyter_boilerplate/snippets_submenus_python/python.js $SNIPPET_DIR .py 19 | npm run convert ./jupyter_boilerplate/snippets_submenus_python/scipy.js $SNIPPET_DIR .py 20 | npm run convert ./jupyter_boilerplate/snippets_submenus_python/h5py.js $SNIPPET_DIR .py 21 | npm run convert ./jupyter_boilerplate/snippets_submenus_python/numba.js $SNIPPET_DIR .py 22 | npm run convert ./jupyter_boilerplate/snippets_submenus_python/sympy.js $SNIPPET_DIR .py 23 | 24 | cd ../binder 25 | 26 | ln -s $SNIPPET_DIR snippets_symlink 27 | 28 | cd .. 29 | 30 | # verify the environment is self-consistent before even starting 31 | python -m pip check 32 | 33 | # install the labextension 34 | python -m pip install . 35 | 36 | # verify the environment the extension didn't break anything 37 | python -m pip check 38 | 39 | # list the extensions 40 | jupyter server extension list 41 | 42 | # initially list installed extensions to determine if there are any surprises 43 | jupyter labextension list 44 | 45 | 46 | echo "JupyterLab with jupyterlab-snippets is ready to run with:" 47 | echo " jupyter lab" -------------------------------------------------------------------------------- /install.json: -------------------------------------------------------------------------------- 1 | { 2 | "packageManager": "python", 3 | "packageName": "jupyterlab-snippets", 4 | "uninstallInstructions": "Use your Python package manager (pip, conda, etc.) to uninstall the package jupyterlab-snippets" 5 | } 6 | -------------------------------------------------------------------------------- /jupyter-boilerplate-converter/README.md: -------------------------------------------------------------------------------- 1 | # jupyter-boilerplate-converter 2 | 3 | Convert snippets from the [jupyter-boilerplate](https://github.com/moble/jupyter_boilerplate) classic notebook 4 | extension (not available for JupyterLab) to [jupyterlab-snippets](../README.md). 5 | 6 | ## Usage 7 | 8 | Run the following commands in the `jupyter-boilerplate-converter` directory. 9 | ``` 10 | $ npm install 11 | $ npm run convert [extension] 12 | ``` 13 | 14 | ## Example 15 | 16 | Convert built-in jupyter-boilerplate snippets. 17 | 18 | Run the following commands in the `jupyter-boilerplate-converter` directory. 19 | 20 | Download jupyter-boilerplate from github and list snippets: 21 | ``` 22 | $ git clone git://github.com/moble/jupyter_boilerplate 23 | $ ls -l jupyter_boilerplate/snippets_submenus_python 24 | 25 | astropy.js matplotlib.js numpy.js numpy_ufuncs.js python.js 26 | scipy.js scipy_special.js sympy_assumptions.js h5py.js numba.js 27 | numpy_polynomial.js pandas.js python_regex.js scipy_constants.js sympy.js 28 | sympy_functions.js 29 | ``` 30 | 31 | Find JupyterLab data directories: 32 | ``` 33 | $ jupyter --paths 34 | 35 | # Output in MacOs, this will be different on Linux and Windows 36 | ... 37 | data: 38 | /Users//Library/Jupyter 39 | /Users//miniconda3/envs//share/jupyter 40 | /usr/local/share/jupyter 41 | /usr/local/share/jupyter 42 | /usr/share/jupyter 43 | ... 44 | ``` 45 | 46 | Convert one of the snippets to JupyterLab user-data directory (top directory in the list above): 47 | ``` 48 | $ npm install # only required to run once 49 | $ npm run convert jupyter_boilerplate/snippets_submenus_python/numpy.js ~/Library/Jupyter .py 50 | ``` 51 | -------------------------------------------------------------------------------- /jupyter-boilerplate-converter/convert.js: -------------------------------------------------------------------------------- 1 | var requirejs = require('requirejs'); 2 | var fs = require('fs'); 3 | var sanitizeFilename = require('sanitize-filename'); 4 | 5 | 6 | const source = process.argv[2]; 7 | const destination = process.argv[3]; 8 | const extension = process.argv[4] || ''; 9 | 10 | requirejs.config({ 11 | nodeRequire: require 12 | }); 13 | require('node-define'); 14 | 15 | sourceData = require(source); 16 | 17 | if (!fs.existsSync(destination)){ 18 | fs.mkdirSync(destination); 19 | } 20 | 21 | function createLevel(path, data) { 22 | if (!data.name) { 23 | return 24 | } 25 | const newPath = `${path}/${sanitizeFilename(data.name)}`; 26 | 27 | if (data.snippet) { 28 | fs.writeFile(`${newPath}${extension}`, data.snippet.join('\n'), function (err) { 29 | if (err) throw err; 30 | }); 31 | } else if (data['sub-menu']) { 32 | if (!fs.existsSync(newPath)){ 33 | fs.mkdirSync(newPath); 34 | } 35 | data['sub-menu'].forEach(submenu => { 36 | createLevel(newPath, submenu); 37 | }); 38 | } 39 | } 40 | 41 | createLevel(destination, sourceData); 42 | -------------------------------------------------------------------------------- /jupyter-boilerplate-converter/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jupyter-boilerplate-converter", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "node-define": { 8 | "version": "0.1.1", 9 | "resolved": "https://registry.npmjs.org/node-define/-/node-define-0.1.1.tgz", 10 | "integrity": "sha1-i3hhdkUQdKn/UVULEsOOfp+gDfM=", 11 | "dev": true 12 | }, 13 | "requirejs": { 14 | "version": "2.3.6", 15 | "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.6.tgz", 16 | "integrity": "sha512-ipEzlWQe6RK3jkzikgCupiTbTvm4S0/CAU5GlgptkN5SO6F3u0UD0K18wy6ErDqiCyP4J4YYe1HuAShvsxePLg==", 17 | "dev": true 18 | }, 19 | "sanitize-filename": { 20 | "version": "1.6.3", 21 | "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz", 22 | "integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==", 23 | "dev": true, 24 | "requires": { 25 | "truncate-utf8-bytes": "^1.0.0" 26 | } 27 | }, 28 | "truncate-utf8-bytes": { 29 | "version": "1.0.2", 30 | "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", 31 | "integrity": "sha1-QFkjkJWS1W94pYGENLC3hInKXys=", 32 | "dev": true, 33 | "requires": { 34 | "utf8-byte-length": "^1.0.1" 35 | } 36 | }, 37 | "utf8-byte-length": { 38 | "version": "1.0.4", 39 | "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz", 40 | "integrity": "sha1-9F8VDExm7uloGGUFq5P8u4rWv2E=", 41 | "dev": true 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /jupyter-boilerplate-converter/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jupyter-boilerplate-converter", 3 | "version": "1.0.0", 4 | "description": "jupyter-boilerplate snippets converter", 5 | "scripts": { 6 | "convert": "node ./convert.js", 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "license": "BSD-3-Clause", 10 | "author": "QuantStack", 11 | "devDependencies": { 12 | "node-define": "^0.1.1", 13 | "requirejs": "^2.3.6", 14 | "sanitize-filename": "^1.6.3" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /jupyter-config/jupyter_notebook_config.d/jupyterlab_snippets.json: -------------------------------------------------------------------------------- 1 | { 2 | "NotebookApp": { 3 | "nbserver_extensions": { 4 | "jupyterlab_snippets": true 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /jupyter-config/jupyter_server_config.d/jupyterlab_snippets.json: -------------------------------------------------------------------------------- 1 | { 2 | "ServerApp": { 3 | "jpserver_extensions": { 4 | "jupyterlab_snippets": true 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /jupyterlab_snippets/__init__.py: -------------------------------------------------------------------------------- 1 | import json 2 | from pathlib import Path 3 | 4 | from ._version import __version__ 5 | from .handlers import setup_handlers 6 | from .loader import SnippetsLoader 7 | 8 | HERE = Path(__file__).parent.resolve() 9 | 10 | with (HERE / "labextension" / "package.json").open() as fid: 11 | data = json.load(fid) 12 | 13 | 14 | def _jupyter_labextension_paths(): 15 | return [{"src": "labextension", "dest": data["name"]}] 16 | 17 | 18 | def _jupyter_server_extension_points(): 19 | return [{"module": "jupyterlab_snippets"}] 20 | 21 | 22 | def _load_jupyter_server_extension(server_app): 23 | """Registers the API handler to receive HTTP requests from the frontend extension. 24 | 25 | Parameters 26 | ---------- 27 | server_app: jupyterlab.labapp.LabApp 28 | JupyterLab application instance 29 | """ 30 | loader = SnippetsLoader() 31 | setup_handlers(server_app.web_app, loader) 32 | 33 | 34 | # For backward compatibility with the classical notebook 35 | load_jupyter_server_extension = _load_jupyter_server_extension 36 | -------------------------------------------------------------------------------- /jupyterlab_snippets/_version.py: -------------------------------------------------------------------------------- 1 | import json 2 | from pathlib import Path 3 | 4 | __all__ = ["__version__"] 5 | 6 | def _fetchVersion(): 7 | HERE = Path(__file__).parent.resolve() 8 | 9 | for settings in HERE.rglob("package.json"): 10 | try: 11 | with settings.open() as f: 12 | return json.load(f)["version"] 13 | except FileNotFoundError: 14 | pass 15 | 16 | raise FileNotFoundError(f"Could not find package.json under dir {HERE!s}") 17 | 18 | __version__ = _fetchVersion() 19 | 20 | -------------------------------------------------------------------------------- /jupyterlab_snippets/handlers.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | import tornado 4 | 5 | from jupyter_server.base.handlers import APIHandler 6 | from jupyter_server.utils import url_path_join 7 | 8 | 9 | class ListSnippets(APIHandler): 10 | def initialize(self, loader): 11 | self.loader = loader 12 | 13 | @tornado.web.authenticated 14 | @tornado.gen.coroutine 15 | def get(self): 16 | snippets = self.loader.collect_snippets() 17 | self.finish(json.dumps(snippets)) 18 | 19 | 20 | class GetSnippet(APIHandler): 21 | def initialize(self, loader): 22 | self.loader = loader 23 | 24 | @tornado.web.authenticated 25 | @tornado.gen.coroutine 26 | def post(self): 27 | data = self.get_json_body() 28 | snippet = data['snippet'] 29 | content = self.loader.get_snippet_content(snippet) 30 | self.finish(json.dumps({ 31 | "content": content 32 | })) 33 | 34 | 35 | def setup_handlers(web_app, loader): 36 | base_url = web_app.settings['base_url'] 37 | handlers = [ 38 | (url_path_join(base_url, 'snippets', 'list'), ListSnippets, {'loader': loader}), 39 | (url_path_join(base_url, 'snippets', 'get'), GetSnippet, {'loader': loader}) 40 | ] 41 | web_app.add_handlers('.*$', handlers) 42 | -------------------------------------------------------------------------------- /jupyterlab_snippets/loader.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from pathlib import PurePath 4 | 5 | from jupyter_core.paths import jupyter_path 6 | 7 | import tornado 8 | 9 | 10 | class SnippetsLoader: 11 | def __init__(self): 12 | self.snippet_paths = jupyter_path("snippets") 13 | 14 | def collect_snippets(self): 15 | snippets = [] 16 | for root_path in self.snippet_paths: 17 | for dirpath, dirnames, filenames in os.walk(root_path, followlinks=True): 18 | # Remove hidden folder 19 | to_remove = [folder for folder in dirnames if folder.startswith(".")] 20 | for folder in to_remove: 21 | dirnames.remove(folder) 22 | 23 | for f in filenames: 24 | fullpath = PurePath(dirpath).relative_to(root_path).joinpath(f) 25 | 26 | if fullpath.parts not in snippets: 27 | snippets.append(fullpath.parts) 28 | 29 | snippets.sort() 30 | return snippets 31 | 32 | def get_snippet_content(self, snippet): 33 | try: 34 | for root_path in self.snippet_paths: 35 | path = os.path.join(root_path, *snippet) 36 | 37 | # Prevent access to the entire file system when the path contains '..' 38 | accessible = os.path.abspath(path).startswith(root_path) 39 | if not accessible: 40 | print(f'jupyterlab-snippets: {path} not accessible from {root_path}') 41 | 42 | if accessible and os.path.isfile(path): 43 | with open(path) as f: 44 | return f.read() 45 | except: 46 | raise tornado.web.HTTPError(status_code=500) 47 | 48 | print(f'jupyterlab-snippets: {snippet} not found in {self.snippet_paths}') 49 | raise tornado.web.HTTPError(status_code=404) 50 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jupyterlab-snippets", 3 | "version": "0.4.1", 4 | "description": "Snippets Extension for JupyterLab", 5 | "keywords": [ 6 | "jupyter", 7 | "jupyterlab", 8 | "jupyterlab-extension" 9 | ], 10 | "homepage": "https://github.com/QuantStack/jupyterlab-snippets", 11 | "bugs": { 12 | "url": "https://github.com/QuantStack/jupyterlab-snippets/issues" 13 | }, 14 | "license": "BSD-3-Clause", 15 | "author": { 16 | "name": "QuantStack", 17 | "email": "" 18 | }, 19 | "files": [ 20 | "lib/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}", 21 | "style/**/*.{css,eot,gif,html,jpg,json,png,svg,woff2,ttf}", 22 | "style/index.js" 23 | ], 24 | "main": "lib/index.js", 25 | "types": "lib/index.d.ts", 26 | "style": "style/index.css", 27 | "repository": { 28 | "type": "git", 29 | "url": "https://github.com/QuantStack/jupyterlab-snippets.git" 30 | }, 31 | "scripts": { 32 | "build": "jlpm run build:lib && jlpm run build:labextension:dev", 33 | "build:all": "jlpm run build:labextension", 34 | "build:labextension": "jupyter labextension build .", 35 | "build:labextension:dev": "jupyter labextension build --development True .", 36 | "build:lib": "tsc", 37 | "build:prod": "jlpm run build:lib && jlpm run build:labextension", 38 | "clean": "jlpm run clean:lib", 39 | "clean:all": "jlpm run clean:lib && jlpm run clean:labextension", 40 | "clean:labextension": "rimraf jupyterlab-snippets/labextension", 41 | "clean:lib": "rimraf lib tsconfig.tsbuildinfo", 42 | "eslint": "eslint . --ext .ts,.tsx --fix", 43 | "eslint:check": "eslint . --ext .ts,.tsx", 44 | "install:extension": "jupyter labextension develop --overwrite .", 45 | "prepare": "jlpm run clean && jlpm run build:prod", 46 | "watch": "run-p watch:src watch:labextension", 47 | "watch:labextension": "jupyter labextension watch .", 48 | "watch:src": "tsc -w" 49 | }, 50 | "dependencies": { 51 | "@jupyterlab/application": "^3.0.4", 52 | "@jupyterlab/apputils": "^3.0.3", 53 | "@jupyterlab/coreutils": "^5.0.2", 54 | "@jupyterlab/mainmenu": "^3.0.3", 55 | "@jupyterlab/notebook": "^3.0.4", 56 | "@jupyterlab/services": "^6.0.3", 57 | "@lumino/commands": "^1.12.0", 58 | "@lumino/widgets": "^1.16.1" 59 | }, 60 | "devDependencies": { 61 | "@jupyterlab/builder": "^3.0.0", 62 | "@typescript-eslint/eslint-plugin": "^4.8.1", 63 | "@typescript-eslint/parser": "^4.8.1", 64 | "eslint": "^7.14.0", 65 | "eslint-config-prettier": "^6.15.0", 66 | "eslint-plugin-prettier": "^3.1.4", 67 | "mkdirp": "^1.0.3", 68 | "npm-run-all": "^4.1.5", 69 | "prettier": "^2.1.1", 70 | "rimraf": "^3.0.2", 71 | "typescript": "~4.1.3" 72 | }, 73 | "sideEffects": [ 74 | "style/*.css", 75 | "style/index.js" 76 | ], 77 | "jupyterlab": { 78 | "discovery": { 79 | "server": { 80 | "managers": [ 81 | "pip" 82 | ], 83 | "base": { 84 | "name": "jupyterlab-snippets" 85 | } 86 | } 87 | }, 88 | "extension": true, 89 | "outputDir": "jupyterlab_snippets/labextension" 90 | }, 91 | "jupyter-releaser": { 92 | "skip": [ 93 | "check-links" 94 | ], 95 | "hooks": { 96 | "before-build-npm": [ 97 | "python -m pip install jupyterlab~=3.1", 98 | "jlpm" 99 | ] 100 | } 101 | }, 102 | "styleModule": "style/index.js" 103 | } 104 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["jupyter_packaging~=0.10", "jupyterlab~=3.0", "tornado~=6.1"] 3 | build-backend = "jupyter_packaging.build_api" 4 | 5 | [license] 6 | file="LICENSE" 7 | 8 | [tool.jupyter-packaging.options] 9 | skip-if-exists = ["jupyterlab_snippets/labextension/static/style.js"] 10 | ensured-targets = ["jupyterlab_snippets/labextension/static/style.js", "jupyterlab_snippets/labextension/package.json"] 11 | 12 | [tool.jupyter-packaging.builder] 13 | factory = "jupyter_packaging.npm_builder" 14 | 15 | [tool.jupyter-packaging.build-args] 16 | build_cmd = "build:prod" 17 | npm = ["jlpm"] 18 | 19 | [tool.check-manifest] 20 | ignore = ["binder/**", "*.json", "*.ipynb", "*.gif", "yarn.lock", ".*", "jupyterlab_snippets/labextension/**", "jupyter-boilerplate-converter/**"] 21 | 22 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | """ 2 | jupyterlab-snippets setup 3 | """ 4 | import json 5 | from pathlib import Path 6 | 7 | import setuptools 8 | 9 | HERE = Path(__file__).parent.resolve() 10 | 11 | # The name of the project 12 | NAME = "jupyterlab-snippets" 13 | PY_NAME = NAME.replace('-', '_') 14 | 15 | lab_path = (HERE / PY_NAME / "labextension") 16 | 17 | # Representative files that should exist after a successful build 18 | ensured_targets = [ 19 | str(lab_path / "package.json"), 20 | str(lab_path / "static/style.js") 21 | ] 22 | 23 | data_files_spec = [ 24 | ("share/jupyter/labextensions/%s" % NAME, str(lab_path), "**"), 25 | ("share/jupyter/labextensions/%s" % NAME, str(HERE), "install.json"), 26 | ("etc/jupyter/jupyter_notebook_config.d", "jupyter-config/jupyter_notebook_config.d", "jupyterlab_snippets.json"), 27 | ("etc/jupyter/jupyter_server_config.d", "jupyter-config/jupyter_server_config.d", "jupyterlab_snippets.json"), 28 | ] 29 | 30 | long_description = (HERE / "README.md").read_text() 31 | 32 | # Get the package info from package.json 33 | pkg_json = json.loads((HERE / "package.json").read_bytes()) 34 | 35 | setup_args = dict( 36 | name=NAME, 37 | version=pkg_json["version"], 38 | url=pkg_json["homepage"], 39 | author=pkg_json["author"]["name"], 40 | author_email=pkg_json["author"]["email"], 41 | description=pkg_json["description"], 42 | license=pkg_json["license"], 43 | long_description=long_description, 44 | long_description_content_type="text/markdown", 45 | packages=setuptools.find_packages(), 46 | install_requires=[ 47 | "jupyterlab~=3.0", 48 | ], 49 | zip_safe=False, 50 | include_package_data=True, 51 | python_requires=">=3.6", 52 | platforms="Linux, Mac OS X, Windows", 53 | keywords=["Jupyter", "JupyterLab", "JupyterLab3"], 54 | classifiers=[ 55 | "License :: OSI Approved :: BSD License", 56 | "Programming Language :: Python", 57 | "Programming Language :: Python :: 3", 58 | "Programming Language :: Python :: 3.6", 59 | "Programming Language :: Python :: 3.7", 60 | "Programming Language :: Python :: 3.8", 61 | "Programming Language :: Python :: 3.9", 62 | "Framework :: Jupyter", 63 | "Framework :: Jupyter :: JupyterLab", 64 | "Framework :: Jupyter :: JupyterLab :: 3", 65 | "Framework :: Jupyter :: JupyterLab :: Extensions", 66 | "Framework :: Jupyter :: JupyterLab :: Extensions :: Prebuilt", 67 | ], 68 | ) 69 | 70 | try: 71 | from jupyter_packaging import ( 72 | wrap_installers, 73 | npm_builder, 74 | get_data_files 75 | ) 76 | post_develop = npm_builder( 77 | build_cmd="build:prod", source_dir="src", build_dir=lab_path 78 | ) 79 | setup_args['cmdclass'] = wrap_installers(post_develop=post_develop, ensured_targets=ensured_targets) 80 | setup_args['data_files'] = get_data_files(data_files_spec) 81 | except ImportError as e: 82 | pass 83 | 84 | if __name__ == "__main__": 85 | setuptools.setup(**setup_args) 86 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | JupyterFrontEnd, 3 | JupyterFrontEndPlugin 4 | } from '@jupyterlab/application'; 5 | 6 | import { PathExt } from '@jupyterlab/coreutils'; 7 | 8 | import { IMainMenu } from '@jupyterlab/mainmenu'; 9 | 10 | import { INotebookTracker, NotebookActions } from '@jupyterlab/notebook'; 11 | 12 | import { CommandRegistry } from '@lumino/commands'; 13 | 14 | import { 15 | MenuSvg, 16 | pythonIcon, 17 | terminalIcon, 18 | textEditorIcon, 19 | folderIcon 20 | } from '@jupyterlab/ui-components'; 21 | 22 | import { listSnippets, Snippet, fetchSnippet } from './snippets'; 23 | 24 | /** 25 | * The command IDs used by the snippets plugin. 26 | */ 27 | namespace CommandIDs { 28 | export const open = 'snippets:open'; 29 | } 30 | 31 | /** 32 | * A tree to represent the list of snippets. 33 | */ 34 | type Tree = Map; 35 | 36 | /** 37 | * Convert the list of snippets a tree. 38 | * @param snippets The list of snippets. 39 | */ 40 | function toTree(snippets: Snippet[]) { 41 | const tree = new Map(); 42 | snippets.forEach(snippet => { 43 | let node = tree; 44 | snippet.forEach(part => { 45 | if (!node.has(part)) { 46 | node.set(part, new Map()); 47 | } 48 | node = node.get(part); 49 | }); 50 | }); 51 | return tree; 52 | } 53 | 54 | /** 55 | * Create a menu from a tree of snippets. 56 | * @param commands The command registry. 57 | * @param tree The tree of snippets. 58 | * @param path The current path in the tree. 59 | */ 60 | function createMenu( 61 | commands: CommandRegistry, 62 | tree: Tree, 63 | path: string[] = [] 64 | ) { 65 | const menu = new MenuSvg({ commands }); 66 | for (const [name, map] of tree.entries()) { 67 | const fullpath = path.concat(name); 68 | if (map.size === 0) { 69 | menu.addItem({ 70 | command: CommandIDs.open, 71 | args: { label: name, path: fullpath } 72 | }); 73 | } else { 74 | const submenu = createMenu(commands, map, path.concat(name)); 75 | submenu.title.label = name; 76 | submenu.title.icon = folderIcon; 77 | menu.addItem({ type: 'submenu', submenu }); 78 | } 79 | } 80 | return menu; 81 | } 82 | 83 | /** 84 | * Initialization data for the jupyterlab-snippets extension. 85 | */ 86 | const extension: JupyterFrontEndPlugin = { 87 | id: 'jupyterlab-snippets', 88 | autoStart: true, 89 | optional: [IMainMenu, INotebookTracker], 90 | activate: async ( 91 | app: JupyterFrontEnd, 92 | menu: IMainMenu | null, 93 | notebookTracker: INotebookTracker | null 94 | ) => { 95 | const { commands } = app; 96 | 97 | const isEnabled = () => { 98 | return ( 99 | notebookTracker?.currentWidget !== null && 100 | notebookTracker?.currentWidget === app.shell.currentWidget 101 | ); 102 | }; 103 | 104 | commands.addCommand(CommandIDs.open, { 105 | label: args => args['label'] as string, 106 | icon: args => { 107 | const ext = PathExt.extname(args['label'] as string); 108 | if (ext === '.py') { 109 | return pythonIcon; 110 | } 111 | if (ext === '.sh') { 112 | return terminalIcon; 113 | } 114 | return textEditorIcon; 115 | }, 116 | execute: async args => { 117 | const path = args['path'] as string[]; 118 | const response = await fetchSnippet(path); 119 | const content = response.content; 120 | 121 | if (!isEnabled()) { 122 | return; 123 | } 124 | 125 | const current = notebookTracker.currentWidget; 126 | const notebook = current.content; 127 | NotebookActions.insertBelow(notebook); 128 | const activeCell = notebook.activeCell; 129 | activeCell.model.value.text = content; 130 | }, 131 | isEnabled 132 | }); 133 | 134 | if (menu) { 135 | const list = await listSnippets(); 136 | const snippetsMenu = createMenu(commands, toTree(list)); 137 | snippetsMenu.title.label = 'Snippets'; 138 | menu.addMenu(snippetsMenu); 139 | } 140 | } 141 | }; 142 | 143 | export default extension; 144 | -------------------------------------------------------------------------------- /src/snippets.ts: -------------------------------------------------------------------------------- 1 | import { URLExt } from '@jupyterlab/coreutils'; 2 | 3 | import { ServerConnection } from '@jupyterlab/services'; 4 | 5 | /** 6 | * The type for a Snippet. 7 | */ 8 | export type Snippet = string[]; 9 | 10 | /** 11 | * The Snippet Content interface 12 | */ 13 | export interface ISnippetContent { 14 | content: string; 15 | } 16 | 17 | /** 18 | * List the available snippets. 19 | */ 20 | export async function listSnippets(): Promise { 21 | return requestAPI('list'); 22 | } 23 | 24 | /** 25 | * Fetch a snippet given its path. 26 | * @param snippet The path of the snippet to fetch. 27 | */ 28 | export async function fetchSnippet(snippet: Snippet): Promise { 29 | const request: RequestInit = { 30 | method: 'POST', 31 | body: JSON.stringify({ snippet }) 32 | }; 33 | return requestAPI('get', request); 34 | } 35 | 36 | /** 37 | * Call the API extension 38 | * 39 | * @param endPoint API REST end point for the extension 40 | * @param init Initial values for the request 41 | * @returns The response body interpreted as JSON 42 | */ 43 | async function requestAPI( 44 | endPoint = '', 45 | init: RequestInit = {} 46 | ): Promise { 47 | const settings = ServerConnection.makeSettings(); 48 | const requestUrl = URLExt.join(settings.baseUrl, 'snippets', endPoint); 49 | 50 | let response: Response; 51 | try { 52 | response = await ServerConnection.makeRequest(requestUrl, init, settings); 53 | } catch (error) { 54 | throw new ServerConnection.NetworkError(error); 55 | } 56 | 57 | const data = await response.json(); 58 | 59 | if (!response.ok) { 60 | throw new ServerConnection.ResponseError(response, data.message); 61 | } 62 | 63 | return data; 64 | } 65 | -------------------------------------------------------------------------------- /style/base.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuantStack/jupyterlab-snippets/7221bd470dab4e50c521845a4fb37e86758673fe/style/base.css -------------------------------------------------------------------------------- /style/index.css: -------------------------------------------------------------------------------- 1 | @import url('base.css'); 2 | -------------------------------------------------------------------------------- /style/index.js: -------------------------------------------------------------------------------- 1 | import './base.css'; 2 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "composite": true, 5 | "declaration": true, 6 | "esModuleInterop": true, 7 | "incremental": true, 8 | "jsx": "react", 9 | "module": "esnext", 10 | "moduleResolution": "node", 11 | "noEmitOnError": true, 12 | "noImplicitAny": true, 13 | "noUnusedLocals": true, 14 | "preserveWatchOutput": true, 15 | "resolveJsonModule": true, 16 | "outDir": "lib", 17 | "rootDir": "src", 18 | "strict": true, 19 | "strictNullChecks": false, 20 | "target": "es2017", 21 | "types": [] 22 | }, 23 | "include": ["src/*"] 24 | } 25 | --------------------------------------------------------------------------------