{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "## Extend jupyterlab-lsp" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### How to add a new LSP feature?\n", "\n", "Features (as well as other parts of the frontend) reuse the\n", "[JupyterLab plugins system](https://jupyterlab.readthedocs.io/en/stable/extension/extension_dev.html#plugins).\n", "Each plugin is a [TypeScript](https://www.typescriptlang.org/) package exporting\n", "one or more `JupyterFrontEndPlugin`s (see\n", "[the JupyterLab extesion developer tutorial](https://jupyterlab.readthedocs.io/en/stable/extension/extension_tutorial.html)\n", "for an overview).\n", "\n", "Each feature has to register itself with the `FeatureManager` (which is provided\n", "after requesting `ILSPFeatureManager` token from `@jupyterlab/lsp`) using\n", "`register(options: IFeatureOptions)` method.\n", "\n", "The feature specification should follow the `IFeature` interface as of\n", "JupyterLab 4.0, including:\n", "\n", "- `id`: unique identifier of the feature, we recommend\n", " `@organization/project:feature` pattern\n", "- `capabilities`: an optional object defining the [client\n", " capabilities][clientcapabilities] implemented by your feature,\n", "\n", "See JupyterLab\n", "[Extension Points >> LSP Features](https://jupyterlab.readthedocs.io/en/latest/extension/extension_points.html#lsp-features)\n", "documentation for more details.\n", "\n", "#### How to override the default implementation of a feature?\n", "\n", "You can specify a list of plugins implementing features which you want to\n", "disable in [`jupyterlab.disabledExtensions`][disabledextensions] stanza of\n", "`package.json`, for example:\n", "\n", "```json\n", "\"jupyterlab\": {\n", " \"disabledExtensions\": [\"@jupyter-lsp/jupyterlab-lsp:hover\"]\n", "}\n", "```\n", "\n", "will disable the hover feature.\n", "\n", "[clientCapabilities]:\n", "https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#clientCapabilities]\n", "[disabledExtensions]:\n", "https://jupyterlab.readthedocs.io/en/latest/extension/extension_dev.html#disabling-other-extensions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### How to add a custom magic or foreign extractor?\n", "\n", "It is now possible to register custom code replacements using\n", "`ILSPCodeOverridesManager` token and to register custom foreign code extractors\n", "using `ILSPCodeExtractorsManager` token, however this API is considered\n", "provisional and subject to change.\n", "\n", "#### Future plans for transclusions handling\n", "\n", "We welcome pull requests enabling kernels to register their custom syntax/code\n", "transformations. The frontend API will remain available for the end-users who\n", "write their custom syntax modifications with actionable side-effects (e.g. a\n", "custom IPython magic which copies a variable from the host document to the\n", "embedded document)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### How to add custom icons for the completer?\n", "\n", "1. Prepare the icons in the SVG format (we use 16 x 16 pixels, but you should be\n", " fine with up to 24 x 24). You can load them for webpack in typescript using\n", " imports if you include a `typings.d.ts` file with the following content:\n", "\n", " ```typescript\n", " declare module '*.svg' {\n", " const script: string;\n", " export default script;\n", " }\n", " ```\n", "\n", " in your `src/`. You should probably keep the icons in your `style/`\n", " directory.\n", "\n", "2. Prepare `CompletionKind` → `IconSvgString` mapping for the light (and\n", " optionally dark) theme, implementing the `ICompletionIconSet` interface. We\n", " have an additional `Kernel` completion kind that is used for completions\n", " provided by kernel that had no recognizable type provided.\n", "\n", "3. Provide all other metadata required by the `ICompletionTheme` interface and\n", " register it on `ILSPCompletionThemeManager` instance using `register_theme()`\n", " method.\n", "\n", "4. Provide any additional CSS styling targeting the JupyterLab completer\n", " elements inside of `.lsp-completer-theme-{id}`, e.g.\n", " `.lsp-completer-theme-material .jp-Completer-icon svg` for the material\n", " theme. Remember to include the styles by importing the in one of the source\n", " files.\n", "\n", "For an example of a complete theme see\n", "[theme-vscode](https://github.com/jupyter-lsp/jupyterlab-lsp/tree/master/packages/theme-vscode)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Migrating to v5.0" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- `IFeature` interface was moved to `@jupyterlab/lsp`\n", " - `labIntegration` was removed,\n", " - `editorIntegrationFactory` was removed in JupyterLab 4.0 and restored in\n", " JupyterLab 4.1 as `extensionFactory` with new API\n", " (`ILSPEditorExtensionFactory`),\n", " - `supersedes` was removed; you can disable extensions using the JupyterLab\n", " native `jupyterlab.disabledExtensions` stanza of `package.json`.\n", "- `ILSPCompletionThemeManager`:\n", " - `register_theme()` was renamed to `registerTheme()`\n", " - all other methods were renamed to follow camelCase convention" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Extend jupyter-lsp\n", "\n", "### Language Server Specs\n", "\n", "Language Server Specs can be [configured](./Configuring) by Jupyter users, or\n", "distributed by third parties as python or JSON files. Since we'd like to see as\n", "many Language Servers work out of the box as possible, consider\n", "[contributing a spec](./Contributing.ipynb#specs), if it works well for you!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Message Listeners\n", "\n", "Message listeners may choose to receive LSP messages immediately after being\n", "received from the client (e.g. `jupyterlab-lsp`) or a language server. All\n", "listeners of a message are scheduled concurrently, and the message is passed\n", "along **once all listeners return** (or fail). This allows listeners to, for\n", "example, modify files on disk before the language server reads them.\n", "\n", "If a listener is going to perform an expensive activity that _shouldn't_ block\n", "delivery of a message, a non-blocking technique like\n", "[IOLoop.add_callback][add_callback] and/or a\n", "[queue](https://www.tornadoweb.org/en/stable/queues.html) should be used.\n", "\n", "[add_callback]:\n", " https://www.tornadoweb.org/en/stable/ioloop.html#tornado.ioloop.IOLoop.add_callback" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Add a Listener with `entry_points`\n", "\n", "Listeners can be added via [entry_points][] by a package installed in the same\n", "environment as `notebook`:\n", "\n", "```toml\n", "## setup.cfg\n", "\n", "[options.entry_points]\n", "jupyter_lsp_listener_all_v1 =\n", " some-unique-name = some.module:some_function\n", "jupyter_lsp_listener_client_v1 =\n", " some-other-unique-name = some.module:some_other_function\n", "jupyter_lsp_listener_server_v1 =\n", " yet-another-unique-name = some.module:yet_another_function\n", "```\n", "\n", "At present, the entry point names generally have no impact on functionality\n", "aside from logging in the event of an error on import.\n", "\n", "[entry_points]: https://packaging.python.org/specifications/entry-points/" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### Add a Listener with Jupyter Configuration\n", "\n", "Listeners can be added via `traitlets` configuration, e.g.\n", "\n", "```yaml\n", "## jupyter_server_config.jsons\n", "{\n", " 'LanguageServerManager':\n", " {\n", " 'all_listeners': ['some.module.some_function'],\n", " 'client_listeners': ['some.module.some_other_function'],\n", " 'server_listeners': ['some.module.yet_another_function']\n", " }\n", "}\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### Add a listener with the Python API\n", "\n", "`lsp_message_listener` can be used as a decorator, accessed as part of a\n", "`serverextension`.\n", "\n", "This listener receives _all_ messages from the client and server, and prints\n", "them out.\n", "\n", "```python\n", "from jupyter_lsp import lsp_message_listener\n", "\n", "def load_jupyter_server_extension(nbapp):\n", "\n", " @lsp_message_listener(\"all\")\n", " async def my_listener(scope, message, language_server, manager):\n", " print(\"received a {} {} message from {}\".format(\n", " scope, message[\"method\"], language_server\n", " ))\n", "```\n", "\n", "`scope` is one of `client`, `server` or `all`, and is required." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### Listener options\n", "\n", "Fine-grained controls are available as part of the Python API. Pass these as\n", "named arguments to `lsp_message_listener`.\n", "\n", "- `language_server`: a regular expression of language servers\n", "- `method`: a regular expression of LSP JSON-RPC method names" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "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.11.4" }, "widgets": { "application/vnd.jupyter.widget-state+json": { "state": {}, "version_major": 2, "version_minor": 0 } } }, "nbformat": 4, "nbformat_minor": 4 }