{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "## Configuring backend" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Configuration Files\n", "\n", "Like the Jupyter Notebook server, JupyterHub, and other Jupyter interactive\n", "computing tools, `jupyter-lsp` can be configured via [Python or JSON\n", "files][notebook-config] in _well-known locations_. You can find out where to put\n", "them on your system with:\n", "\n", "[notebook-config]: https://jupyter-notebook.readthedocs.io/en/stable/config.html\n", "\n", "```bash\n", "jupyter --paths\n", "```\n", "\n", "They will be merged from bottom to top, and the directory where you launch your\n", "`notebook` or `lab` server wins, making it easy to check in to version control." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Configuration Options" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### language_servers\n", "\n", "`jupyter-lsp` does not come with any Language Servers! However, we will try to\n", "use [known language servers](./Language%20Servers.html) if they _are_ installed\n", "and we know about them: you can disable this behavior by configuring\n", "[autodetect](#autodetect).\n", "\n", "If you don't see an implementation for the language server you need, continue\n", "reading!\n", "\n", "> Please consider [contributing your language server spec](./Contributing.html)\n", "> to `jupyter-lsp`!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The absolute minimum language server spec requires:\n", "\n", "- `argv`, a list of _shell tokens_ to launch the server in `stdio` mode (as\n", " opposed to `tcp`),\n", " - _shell tokens_ are arrays of strings representing command line commands with\n", " arguments, for example `ls -l` is represented as `[\"ls\", \"-l\"]` while\n", " `mkdir \"new directory\"` should be split into `[\"mkdir\", \"new directory\"]`;\n", " If you have Python installed, you can use `shlex.split(\"your command\")` to\n", " get such an array.\n", "- the `languages` which the language server will respond to, and\n", "- the schema `version` of the spec (currently `2`)\n", "- `mime_types` by which the notebooks and files will be matched to the language\n", " server:\n", " - for notebooks the MIME type is derived from `language_info`/`mimetype`\n", " element of [kernel_info][] response (with fallback on to cell metadata if\n", " missing from kernel response)\n", " - for files the implementation is frontend-specific; in JupyterLab the MIME\n", " type is obtained from the MIME type registry.\n", "\n", "```python\n", "# ./jupyter_server_config.json ---------- unique! -----------\n", "# | |\n", "# or e.g. V V\n", "# $PREFIX/etc/jupyter/jupyter_server_config.d/a-language-server-implementation.json\n", "{\n", " \"LanguageServerManager\": {\n", " \"language_servers\": {\n", " \"a-language-server-implementation\": {\n", " \"version\": 2,\n", " \"argv\": [\"/absolute/path/to/a-language-server\", \"--stdio\"],\n", " \"languages\": [\"a-language\"],\n", " \"mime_types\": [\"text/language\", \"text/x-language\"]\n", " }\n", " }\n", " }\n", "}\n", "```\n", "\n", "[kernel_info]:\n", " https://jupyter-client.readthedocs.io/en/stable/messaging.html#kernel-info" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A number of other options we hope to use to enrich the user experience are\n", "available in the [schema][].\n", "\n", "[schema]:\n", " https://github.com/krassowski/jupyterlab-lsp/blob/master/python_packages/jupyter_lsp/jupyter_lsp/schema/schema.json\n", "\n", "More complex configurations that can't be hard-coded may benefit from the python\n", "approach:\n", "\n", "```python\n", "# jupyter_server_config.py\n", "import shutil\n", "\n", "# c is a magic, lazy variable\n", "c.LanguageServerManager.language_servers = {\n", " \"a-language-server-implementation\": {\n", " # if installed as a binary\n", " \"argv\": [shutil.which(\"a-language-server\")],\n", " \"languages\": [\"a-language\"],\n", " \"version\": 2,\n", " \"mime_types\": [\"text/a-language\"]\n", " },\n", " \"another-language-implementation\": {\n", " # if run like a script\n", " \"argv\": [shutil.which(\"another-language-interpreter\"), \"another-language-server\"],\n", " \"languages\": [\"another-language\"],\n", " \"version\": 2,\n", " \"mime_types\": [\"text/another-language\"]\n", " }\n", "}\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### nodejs\n", "\n", "> default: `None`\n", "\n", "An absolute path to your `nodejs` executable. If `None`, `nodejs` will be\n", "detected in a number of well-known places." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### autodetect\n", "\n", "> default: `True`\n", "\n", "If `True`, `jupyter-lsp` will look for all\n", "[known language servers](./Language%20Servers.html). User-configured\n", "`language_servers` of the same implementation will be preferred over\n", "`autodetect`ed ones." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### node_roots\n", "\n", "> default: `[]`\n", "\n", "Absolute paths to search for directories named `node_modules`, such as\n", "`nodejs`-backed language servers. The order is, roughly:\n", "\n", "- the folder where `notebook` or `lab` was launched\n", "- the JupyterLab `staging` folder\n", "- wherever `conda` puts global node modules\n", "- wherever some other conventions put it" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### extra_node_roots\n", "\n", "> default: `[]`\n", "\n", "Additional places `jupyter-lsp` will look for `node_modules`. These will be\n", "checked _before_ `node_roots`, and should not contain the trailing\n", "`node_modules`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### virtual_documents_dir\n", "\n", "> default: os.getenv(\"JP_LSP_VIRTUAL_DIR\", \".virtual_documents\")\n", "\n", "Path to virtual documents relative to the content manager root directory.\n", "\n", "Its default value can be set with `JP_LSP_VIRTUAL_DIR` environment variable and\n", "fallback to `.virtual_documents`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Python `entry_points`\n", "\n", "`pip`-installable packages in the same environment as the Jupyter server can be\n", "automatically detected as providing [language_servers](#language_servers). These\n", "are a little more involved, but also more powerful: see more in\n", "[Contributing](Contributing.html#Specs). Servers configured this way are loaded\n", "_before_ those defined in [configuration files](#Configuration-Files), so that a\n", "user can fine-tune their available servers." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Making Custom Servers Work With Notebooks\n", "\n", "To enable integration of language servers with Jupyter notebooks this extensions\n", "assumes that the `language_info` section of [`kernel_info_reply`][kernel_info]\n", "is complete and properly returned by the Kernel. In particular the following\n", "elements are required:\n", "\n", "- **File extension**: many language servers only handle files with specific file\n", " extensions and refuse to operate if not provided with such; the file extension\n", " of a native script for a given language (this is other than `.ipynb`), derived\n", " from `file_extension` field of `language_info`, will be added to the name of\n", " the notebook when communicating with the language server to satisfy the file\n", " extension check.\n", "- **MIME type**: matching of notebooks to servers is based on the MIME types\n", " declared in server specification files and `mimetype` field of\n", " `language_info`. If kernel fails to provide any MIME type, connecting the\n", " language server will not be possible; if multiple MIME types are in use, any\n", " would work well for this extension as long as you also include it in the\n", " `mime_types` list of language server specification." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Example: Scala Language Server (metals) integration\n", "\n", "**Step 1:** Get a Scala-based kernel installed.\n", "\n", "2 possible options: Almond kernel or the Spark magic kernel.\n", "\n", "Almond kernel install:\n", "\n", "```bash\n", "$ curl -Lo coursier https://git.io/coursier-cli\n", "$ chmod +x coursier\n", "$ ./coursier launch --fork almond -- --install\n", "$ rm -f coursier\n", "```\n", "\n", "Spark Magic kernel:\n", "\n", "```bash\n", "pip install sparkmagic\n", "```\n", "\n", "Now, install the spark kernel:\n", "\n", "```bash\n", "jupyter-kernelspec install sparkmagic/kernels/sparkkernel\n", "```\n", "\n", "**Step 2:** Install metals server in the working directory:\n", "\n", "Metals has a coursier based installation.\n", "\n", "```bash\n", "curl -Lo coursier https://git.io/coursier-cli && chmod +x coursier\n", "./coursier bootstrap org.scalameta:metals_2.12:0.7.0 --force-fetch -o metals -f\n", "```\n", "\n", "(Might need to use the `--force-fetch` flag if you are getting dependency\n", "issues.)\n", "\n", "**Step 3:** Configure the metals server in jupyterlab-lsp. Enter the following\n", "in the `jupyter/jupyter_server_config.d/metals-ls.json` (in one of the jupyter\n", "configuration directories):\n", "\n", "```python\n", "{\n", " \"LanguageServerManager\": {\n", " \"language_servers\": {\n", " \"metals\": {\n", " \"version\": 2,\n", " \"argv\": [\"<$path_to_metals_server(eg:/Users/skakker/projects/jupyterlab-lsp/metals)>\"],\n", " \"languages\": [\"scala\"],\n", " \"mime_types\": [\"text/x-scala\"]\n", " }\n", " }\n", " }\n", "}\n", "```\n", "\n", "You are good to go now! Just start `jupyter lab` and create a notebook with\n", "either the Spark or the Scala kernel and the metals server should automatically\n", "initialise (the status indicator should show \"Fully initialized\")." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.9" } }, "nbformat": 4, "nbformat_minor": 4 }