Configuring backend

Configuration Files

Like the Jupyter Notebook server, JupyterHub, and other Jupyter interactive computing tools, jupyter-lsp can be configured via Python or JSON files in well-known locations. You can find out where to put them on your system with:

jupyter --paths

They will be merged from bottom to top, and the directory where you launch your notebook or lab server wins, making it easy to check in to version control.

Configuration Options

language_servers

jupyter-lsp does not come with any Language Servers! However, we will try to use known language servers if they are installed and we know about them: you can disable this behavior by configuring autodetect.

If you don’t see an implementation for the language server you need, continue reading!

Please consider contributing your language server spec to jupyter-lsp!

The absolute minimum language server spec requires:

  • argv, a list of shell tokens to launch the server in stdio mode (as opposed to tcp),

    • shell tokens are arrays of strings representing command line commands with arguments, for example ls -l is represented as ["ls", "-l"] while mkdir "new directory" should be split into ["mkdir", "new directory"]; If you have Python installed, you can use shlex.split("your command") to get such an array.

  • the languages which the language server will respond to, and

  • the schema version of the spec (currently 2)

  • mime_types by which the notebooks and files will be matched to the language server:

    • for notebooks the MIME type is derived from language_info/mimetype element of kernel_info response (with fallback on to cell metadata if missing from kernel response)

    • for files the implementation is frontend-specific; in JupyterLab the MIME type is obtained from the MIME type registry.

# ./jupyter_server_config.json                   ---------- unique! -----------
#                                               |                              |
# or e.g.                                       V                              V
# $PREFIX/etc/jupyter/jupyter_server_config.d/a-language-server-implementation.json
{
  "LanguageServerManager": {
    "language_servers": {
      "a-language-server-implementation": {
        "version": 2,
        "argv": ["/absolute/path/to/a-language-server", "--stdio"],
        "languages": ["a-language"],
        "mime_types": ["text/language", "text/x-language"]
      }
    }
  }
}

A number of other options we hope to use to enrich the user experience are available in the schema.

More complex configurations that can’t be hard-coded may benefit from the python approach:

# jupyter_server_config.py
import shutil

# c is a magic, lazy variable
c.LanguageServerManager.language_servers = {
    "a-language-server-implementation": {
        # if installed as a binary
        "argv": [shutil.which("a-language-server")],
        "languages": ["a-language"],
        "version": 2,
        "mime_types": ["text/a-language"]
    },
    "another-language-implementation": {
        # if run like a script
        "argv": [shutil.which("another-language-interpreter"), "another-language-server"],
        "languages": ["another-language"],
        "version": 2,
        "mime_types": ["text/another-language"]
    }
}

nodejs

default: None

An absolute path to your nodejs executable. If None, nodejs will be detected in a number of well-known places.

autodetect

default: True

If True, jupyter-lsp will look for all known language servers. User-configured language_servers of the same implementation will be preferred over autodetected ones.

node_roots

default: []

Absolute paths to search for directories named node_modules, such as nodejs-backed language servers. The order is, roughly:

  • the folder where notebook or lab was launched

  • the JupyterLab staging folder

  • wherever conda puts global node modules

  • wherever some other conventions put it

extra_node_roots

default: []

Additional places jupyter-lsp will look for node_modules. These will be checked before node_roots, and should not contain the trailing node_modules.

virtual_documents_dir

default: os.getenv(“JP_LSP_VIRTUAL_DIR”, “.virtual_documents”)

Path to virtual documents relative to the content manager root directory.

Its default value can be set with JP_LSP_VIRTUAL_DIR environment variable and fallback to .virtual_documents.

Python entry_points

pip-installable packages in the same environment as the Jupyter server can be automatically detected as providing language_servers. These are a little more involved, but also more powerful: see more in Contributing. Servers configured this way are loaded before those defined in configuration files, so that a user can fine-tune their available servers.

Making Custom Servers Work With Notebooks

To enable integration of language servers with Jupyter notebooks this extensions assumes that the language_info section of kernel_info_reply is complete and properly returned by the Kernel. In particular the following elements are required:

  • File extension: many language servers only handle files with specific file extensions and refuse to operate if not provided with such; the file extension of a native script for a given language (this is other than .ipynb), derived from file_extension field of language_info, will be added to the name of the notebook when communicating with the language server to satisfy the file extension check.

  • MIME type: matching of notebooks to servers is based on the MIME types declared in server specification files and mimetype field of language_info. If kernel fails to provide any MIME type, connecting the language server will not be possible; if multiple MIME types are in use, any would work well for this extension as long as you also include it in the mime_types list of language server specification.

Example: Scala Language Server (metals) integration

Step 1: Get a Scala-based kernel installed.

2 possible options: Almond kernel or the Spark magic kernel.

Almond kernel install:

$ curl -Lo coursier https://git.io/coursier-cli
$ chmod +x coursier
$ ./coursier launch --fork almond -- --install
$ rm -f coursier

Spark Magic kernel:

pip install sparkmagic

Now, install the spark kernel:

jupyter-kernelspec install sparkmagic/kernels/sparkkernel

Step 2: Install metals server in the working directory:

Metals has a coursier based installation.

curl -Lo coursier https://git.io/coursier-cli && chmod +x coursier
./coursier bootstrap org.scalameta:metals_2.12:0.7.0 --force-fetch -o metals -f

(Might need to use the --force-fetch flag if you are getting dependency issues.)

Step 3: Configure the metals server in jupyterlab-lsp. Enter the following in the jupyter/jupyter_server_config.d/metals-ls.json (in one of the jupyter configuration directories):

{
  "LanguageServerManager": {
    "language_servers": {
      "metals": {
        "version": 2,
        "argv": ["<$path_to_metals_server(eg:/Users/skakker/projects/jupyterlab-lsp/metals)>"],
        "languages": ["scala"],
        "mime_types": ["text/x-scala"]
      }
    }
  }
}

You are good to go now! Just start jupyter lab and create a notebook with either the Spark or the Scala kernel and the metals server should automatically initialise (the status indicator should show “Fully initialized”).