From f94824db80eae9eb574560e9f03b45aa13434279 Mon Sep 17 00:00:00 2001 From: Kitson Kelly Date: Fri, 10 Dec 2021 11:43:38 +1100 Subject: [PATCH 1/2] migrate lsp docs to manual and update for v2 registry completions --- language_server.md | 14 ++ language_server/imports.md | 342 ++++++++++++++++++++++++++++++++++++ language_server/overview.md | 182 +++++++++++++++++++ toc.json | 7 + 4 files changed, 545 insertions(+) create mode 100644 language_server.md create mode 100644 language_server/imports.md create mode 100644 language_server/overview.md diff --git a/language_server.md b/language_server.md new file mode 100644 index 00000000..965c84a3 --- /dev/null +++ b/language_server.md @@ -0,0 +1,14 @@ +# The Language Server + +The Deno CLI comes with a built in language server that can provide an +intelligent editing experience as well as a way to easily access the other tools +that come built in with Deno. For most users, using the language server would be +via your editor like [Visual Studio Code](./vscode_deno.md) or +[other editors](./getting_started/setup_your_environment.md). This section of +the manual is designed for those creating integrations to the language server or +providing a package registry for Deno that integrates intelligently. + +In this section we will cover: + +- [An overview of the language server](./language_server/overview.md) +- [Import completions and intelligent package registries](./language_server/imports.md) diff --git a/language_server/imports.md b/language_server/imports.md new file mode 100644 index 00000000..e847f270 --- /dev/null +++ b/language_server/imports.md @@ -0,0 +1,342 @@ +## Import completions and intelligent registries + +The language server, supports completions for URLs. + +### Local import completions + +When attempting to import a relative module specifier (one that starts with `./` +or `../`), import completions are provided for directories and files that Deno +thinks it can run (ending with the extensions `.ts`, `.js`, `.tsx`, `.jsx`, or +`.mjs`). + +### Workspace import completions + +When attempting to import a remote URL that isn't configured as a registry (see +below), the extension will provide remote modules that are already part of the +workspace. + +### Module registry completions + +Module registries that support it can be configured for auto completion. This +provides a handy way to explore a module registry from the "comfort" of your +IDE. + +#### Auto-discovery + +The Deno language server, by default, will attempt to determine if a server +supports completion suggestions. If the host/origin has not been explicitly +configured, it will check the server, and if it supports completion suggestions +you will be prompted to choose to enable it or not. + +You should only enable this for registries you trust, as the remote server could +provide suggestions for modules which are an attempt to get you to run +un-trusted code. + +#### Configuration + +Settings for configuring registries for auto completions: + +- `deno.suggest.imports.autoDiscover` - If enabled, when the language server + discovers a new origin that isn't explicitly configured, it will check to see + if that origin supports import completions and prompt you to enable it or not. + This is `true` by default. +- `deno.suggest.imports.hosts` - These are the _origins_ that are configured to + provide import completions. The target host needs to support Deno import + completions (detailed below). The value is an object where the key is the host + and the value is if it is enabled or not. For example: + + ```json + { + "deno.suggest.imports.hosts": { + "https://deno.land": true + } + } + ``` + +#### How does it work? + +On startup of the extension and language server, Deno will attempt to fetch +`/.well-known/deno-import-intellisense.json` from any of the hosts that are +configured and enabled. This file provides the data necessary to form auto +completion of module specifiers in a highly configurable way (meaning you aren't +tied into any particular module registry in order to get a rich editor +experience). + +As you build or edit your module specifier, Deno will go and fetch additional +parts of the URL from the host based on what is configured in the JSON +configuration file. + +When you complete the module specifier, if it isn't already cached locally for +you, Deno will attempt to fetch the completed specifier from the registry. + +#### Does it work with all remote modules? + +No, as the extension and Deno need to understand how to _find_ modules. The +configuration file provides a highly flexible way to allow people to describe +how to build up a URL, including supporting things like semantic versioning if +the module registry supports it. + +### Registry support for import completions + +In order to support having a registry be discoverable by the Deno language +server, the registry needs to provide a few things: + +- A schema definition file. This file needs to be located at + `/.well-known/deno-import-intellisense.json`. This file provides the + configuration needed to allow the Deno language server _query_ the registry + and construct import specifiers. +- A series of API endpoints that provide the values to be provided to the user + to complete an import specifier. + +#### Configuration schema + +The JSON response to the schema definition needs to be an object with two +required properties: + +- `"version"` - a number, which must be equal to `1` or `2`. +- `"registries"` - an array of registry objects which define how the module + specifiers are constructed for this registry. + +[There is a JSON Schema document which defines this +schema available as part of the CLI's source code.](https://deno.land/x/deno/cli/schemas/registry-completions.v2.json) + +While the v2 supports more features than v1 did, they were introduced in a +non-breaking way, and the language server automatically handles v1 or v2 +versions, irrespective of what version is supplied in the `"version"` key, so +technically a registry could profess itself to be v1 but use all the v2 +features. This is not recommended though, because while there is no specific +branches in code to support the v2 features currently, that doesn't mean there +will not be in the future in order to support a _v3_ or whatever. + +#### Registries + +In the configuration schema, the `"registries"` property is an array of +registries, which are objects which contain two required properties: + +- `"schema"` - a string, which is an Express-like path matching expression, + which defines how URLs are built on the registry. The syntax is directly based + on [path-to-regexp](https://github.com/pillarjs/path-to-regexp). For example, + if the following was the specifier for a URL on the registry: + + ``` + https://example.com/a_package@v1.0.0/mod.ts + ``` + + The schema value might be something like this: + + ```json + { + "version": 1, + "registries": [ + { + "schema": "/:package([a-z0-9_]*)@:version?/:path*" + } + ] + } + ``` + +- `"variables"` - for the keys defined in the schema, a corresponding variable + needs to be defined, which informs the language server where to fetch + completions for that part of the module specifier. In the example above, we + had 3 variables of `package`, `version` and `path`, so we would expect a + variable definition for each. + +#### Variables + +In the configuration schema, the `"variables"` property is an array of variable +definitions, which are objects with two required properties: + +- `"key"` - a string which matches the variable key name specifier in the + `"schema"` property. +- `"documentation"` - An optional URL where the language server can fetch the + documentation for an individual variable entry. Variables can be substituted + in to build the final URL. Variables with a single brace format like + `${variable}` will be added as matched out of the string, and those with + double format like `${{variable}}` will be percent-encoded as a URI component + part. +- `"url"` - A URL where the language server can fetch the completions for the + variable. Variables can be substituted in to build the URL. Variables with a + single brace format like `${variable}` will be added as matched out of the + string, and those with double format like `${{variable}}` will be + percent-encoded as a URI component part. If the variable the value of the + `"key"` is included, then the language server will support incremental + requests for partial modules, allowing the server to provide completions as a + user types part of the variable value. If the URL is not fully qualified, the + URL of the schema file will be used as a base. In our example above, we had + three variables and so our variable definition might look like: + + ```json + { + "version": 1, + "registries": [ + { + "schema": "/:package([a-z0-9_]*)@:version?/:path*", + "variables": [ + { + "key": "package", + "documentation": "https://api.example.com/docs/packages/${package}", + "url": "https://api.example.com/packages/${package}" + }, + { + "key": "version", + "url": "https://api.example.com/packages/${package}/versions" + }, + { + "key": "path", + "documentation": "https://api.example.com/docs/packages/${package}/${{version}}/paths/${path}", + "url": "https://api.example.com/packages/${package}/${{version}}/paths/${path}" + } + ] + } + ] + } + ``` + +##### URL endpoints + +The response from each URL endpoint needs to be a JSON document that is an array +of strings or a _completions list_: + +```typescript +interface CompletionList { + /** The list (or partial list) of completion items. */ + items: string[]; + /** If the list is a partial list, and further queries to the endpoint will + * change the items, set `isIncomplete` to `true`. */ + isIncomplete?: boolean; + /** If one of the items in the list should be preselected (the default + * suggestion), then set the value of `preselect` to the value of the item. */ + preselect?: string; +} +``` + +Extending our example from above the URL `https://api.example.com/packages` +would be expected to return something like: + +```json +[ + "a_package", + "another_package", + "my_awesome_package" +] +``` + +Or something like this: + +```json +{ + "items": [ + "a_package", + "another_package", + "my_awesome_package" + ], + "isIncomplete": false, + "preselect": "a_package" +} +``` + +And a query to `https://api.example.com/packages/a_package/versions` would +return something like: + +```json +[ + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v2.0.0" +] +``` + +Or: + +```json +{ + "items": [ + "v1.0.0", + "v1.0.1", + "v1.1.0", + "v2.0.0" + ], + "preselect": "v2.0.0" +} +``` + +And a query to +`https://api.example.com/packages/a_package/versions/v1.0.0/paths` would return +something like: + +```json +[ + "a.ts", + "b/c.js", + "d/e.ts" +] +``` + +Or: + +```json +{ + "items": [ + "a.ts", + "b/c.js", + "d/e.ts" + ], + "isIncomplete": true, + "preselect": "a.ts" +} +``` + +##### Documentation endpoints + +Documentation endpoints should return a documentation object with any +documentation related to the requested entity: + +```typescript +interface Documentation { + kind: "markdown" | "plaintext"; + value: string; +} +``` + +For extending the example from above, a query to +`https://api.example.com/packages/a_package` would return something like: + +```json +{ + "kind": "markdown", + "value": "some _markdown_ `documentation` here..." +} +``` + +#### Schema validation + +When the language server is started up (or the configuration for the extension +is changed) the language server will attempt to fetch and validate the schema +configuration for the domain hosts specifier in the configuration. + +The validation attempts to make sure that all registries defined are valid, that +the variables contained in those schemas are specified in the variables, and +that there aren't extra variables defined that are not included in the schema. +If the validation fails, the registry won't be enabled and the errors will be +logged to the Deno Language Server output in vscode. + +If you are a registry maintainer and need help, advice, or assistance in setting +up your registry for auto-completions, feel free to open up an +[issue](https://github.com/denoland/deno/issues/new?labels=lsp&title=lsp%3A%20registry%20configuration) +and we will try to help. + +### Known registries + +The following is a list of registries known to support the scheme. All you need +to do is add the domain to `deno.suggest.imports.hosts` and set the value to +`true`: + +- `https://deno.land/` - both the 3rd party `/x/` registry and the `/std/` + library registry are available. +- `https://nest.land/` - a module registry for Deno on the blockweave. +- `https://crux.land/` - a free open-source registry for permanently hosting + small scripts. + +``` +``` diff --git a/language_server/overview.md b/language_server/overview.md new file mode 100644 index 00000000..0eea3add --- /dev/null +++ b/language_server/overview.md @@ -0,0 +1,182 @@ +## Language server overview + +The Deno Language Server provides a server implementation of the +[Language Server Protocol](https://microsoft.github.io/language-server-protocol/) +which is specifically tailored to provide a _Deno_ view of code. It is +integrated into the command line and can be started via the `lsp` sub-command. + +Most users will never interact with the server directly, but instead will via +[`vscode_deno`](../vscode_deno.md) or another +[editor extension](../getting_started/setup_your_environment.md). This +documentation is for those implementing a editor client. + +### Structure + +When the language server is started, a `LanguageServer` instance is created +which holds all of the state of the language server. It also defines all of the +methods that the client calls via the Language Server RPC protocol. + +### Settings + +There are several settings that the language server supports for a workspace: + +- `deno.enable` +- `deno.cache` +- `deno.config` +- `deno.importMap` +- `deno.internalDebug` +- `deno.codeLens.implementations` +- `deno.codeLens.references` +- `deno.codeLens.referencesAllFunctions` +- `deno.codeLens.test` +- `deno.suggest.completeFunctionCalls` +- `deno.suggest.names` +- `deno.suggest.paths` +- `deno.suggest.autoImports` +- `deno.suggest.imports.autoDiscover` +- `deno.suggest.imports.hosts` +- `deno.lint` +- `deno.unstable` + +There are settings that are supported on a per resource basis by the language +server: + +- `deno.enable` +- `deno.codeLens.test` + +There are several points in the process where Deno analyzes these settings. +First, when the `initialize` request from the client, the +`initializationOptions` will be assumed to be an object that represents the +`deno` namespace of options. For example, the following value: + +```json +{ + "enable": true, + "unstable": true +} +``` + +Would enable Deno with the unstable APIs for this instance of the language +server. + +When the language server receives a `workspace/didChangeConfiguration` +notification, it will assess if the client has indicated if it has a +`workspaceConfiguration` capability. If it does, it will send a +`workspace/configuration` request which will include a request for the workspace +configuration as well as the configuration of all URIs that the language server +is currently tracking. + +If the client has the `workspaceConfiguration` capability, the language server +will send a configuration request for the URI when it received the +`textDocument/didOpen` notification in order to get the resources specific +settings. + +If the client does not have the `workspaceConfiguration` capability, the +language server will assume the workspace setting applies to all resources. + +### Commands + +There are several commands that might be issued by the language server to the +client, which the client is expected to implement: + +- `deno.cache` - This is sent as a resolution code action when there is an + un-cached module specifier that is being imported into a module. It will be + sent with and argument that contains the resolved specifier as a string to be + cached. +- `deno.showReferences` - This is sent as the command on some code lenses to + show locations of references. The arguments contain the specifier that is the + subject of the command, the start position of the target and the locations of + the references to show. +- `deno.test` - This is sent as part of a test code lens to, of which the client + is expected to run a test based on the arguments, which are the specifier the + test is contained in and the name of the test to filter the tests on. + +### Requests + +The LSP currently supports the following custom requests. A client should +implement these in order to have a fully functioning client that integrates well +with Deno: + +- `deno/cache` - This command will instruct Deno to attempt to cache a module + and all of its dependencies. If a `referrer` only is passed, then all + dependencies for the module specifier will be loaded. If there are values in + the `uris`, then only those `uris` will be cached. + + It expects parameters of: + + ```ts + interface CacheParams { + referrer: TextDocumentIdentifier; + uris: TextDocumentIdentifier[]; + } + ``` +- `deno/performance` - Requests the return of the timing averages for the + internal instrumentation of Deno. + + It does not expect any parameters. +- `deno/reloadImportRegistries` - Reloads any cached responses from import + registries. + + It does not expect any parameters. +- `deno/virtualTextDocument` - Requests a virtual text document from the LSP, + which is a read only document that can be displayed in the client. This allows + clients to access documents in the Deno cache, like remote modules and + TypeScript library files built into Deno. The Deno language server will encode + all internal files under the custom schema `deno:`, so clients should route + all requests for the `deno:` schema back to the `deno/virtualTextDocument` + API. + + It also supports a special URL of `deno:/status.md` which provides a markdown + formatted text document that contains details about the status of the LSP for + display to a user. + + It expects parameters of: + + ```ts + interface VirtualTextDocumentParams { + textDocument: TextDocumentIdentifier; + } + ``` + +### Notifications + +There is currently one custom notification that is sent from the server to the +client: + +- `deno/registryState` - when `deno.suggest.imports.autoDiscover` is `true` and + an origin for an import being added to a document is not explicitly set in + `deno.suggest.imports.hosts`, the origin will be checked and the notification + will be sent to the client of the status. + + When receiving the notification, if the param `suggestion` is `true`, the + client should offer the user the choice to enable the origin and add it to the + configuration for `deno.suggest.imports.hosts`. If `suggestion` is `false` the + client should add it to the configuration of as `false` to stop the language + server from attempting to detect if suggestions are supported. + + The params for the notification are: + + ```ts + interface RegistryStatusNotificationParams { + origin: string; + suggestions: boolean; + } + ``` + +### Language IDs + +The language server supports diagnostics and formatting for the following +[text document language IDs](https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentItem): + +- `"javascript"` +- `"javascriptreact"` +- `"jsx"` _non standard, same as `javascriptreact`_ +- `"typescript"` +- `"typescriptreact"` +- `"tsx"` _non standard, same as `typescriptreact`_ + +The language server supports only formatting for the following language IDs: + +- `"json"` +- `"jsonc"` +- `"markdown"` diff --git a/toc.json b/toc.json index 8df1933f..b4dbe949 100644 --- a/toc.json +++ b/toc.json @@ -122,6 +122,13 @@ "vscode_deno": { "name": "Using Visual Studio Code" }, + "language_server": { + "name": "Language Server", + "children": { + "overview": "Overview of the language server", + "imports": "Import suggestions and intelligent registries" + } + }, "embedding_deno": { "name": "Embedding Deno" }, From 6f9489683eba10a2ab4380d84ddd04c9c0ef860b Mon Sep 17 00:00:00 2001 From: Kitson Kelly Date: Wed, 15 Dec 2021 08:29:37 +1100 Subject: [PATCH 2/2] remove empty code block --- language_server/imports.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/language_server/imports.md b/language_server/imports.md index e847f270..5e47ac1a 100644 --- a/language_server/imports.md +++ b/language_server/imports.md @@ -337,6 +337,3 @@ to do is add the domain to `deno.suggest.imports.hosts` and set the value to - `https://nest.land/` - a module registry for Deno on the blockweave. - `https://crux.land/` - a free open-source registry for permanently hosting small scripts. - -``` -```