Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docs: new redaction of Standalone-Server-(tsserver).md #329

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 57 additions & 41 deletions Standalone-Server-(tsserver).md
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
The TypeScript standalone server (aka `tsserver`) is a node executable that encapsulates the TypeScript compiler and language services, and exposes them through a JSON protocol. `tsserver` is well suited for editors and IDE support.
The TypeScript standalone server (a.k.a. `tsserver`) is a node executable that encapsulates the TypeScript compiler and language services, and exposes them through a JSON protocol. `tsserver` is well suited for editors and IDE support.

# Protocol

## Definition

The server communication protocol is defined in the `ts.server.protocol` namespace, declared in [`tsserverlibrary.d.ts`](https://github.com/microsoft/TypeScript/blob/main/lib/tsserverlibrary.d.ts).
The server communication protocol is defined in the `ts.server.protocol` namespace, declared in [`protocol.ts`](https://github.com/microsoft/TypeScript/blob/main/src/server/protocol.ts).

The executable can be found in lib folder under the typescript package.
The executable (`tsserver`) can be found in the **bin** folder under the **typescript** package.

```cmd
npm install --save typescript
ls node_modules\typescript\lib\tsserver.js
ls node_modules\typescript\bin\tsserver
```

## Message format
Expand All @@ -33,9 +33,9 @@ Content-Length: 116
{"seq":0,"type":"response","command":"quickinfo","request_seq":2,"success":false,"message":"No content available."}
```

Similarly events have the same format as a response.
Similarly, responses to events also have the same format.

Here is an example event for error message:
Here is an example event for an error message:

```js
Content-Length: 261
Expand All @@ -45,9 +45,9 @@ Content-Length: 261

## Commands

`tsserver` supports a set of commands. The full list of commands supported by the server can be found under [ts.server.protocol.CommandTypes](https://github.com/microsoft/TypeScript/blob/main/src/server/protocol.ts).
The full list of commands supported by `tsserver` can be found under [ts.server.protocol.CommandTypes](https://github.com/microsoft/TypeScript/blob/main/src/server/protocol.ts).

Each command is associated with a request and a response interface. For instance command `"completions"` corresponds to request interface `CompletionsRequest`, and response interface defined in `CompletionsResponse`.
Each command is associated with a request and a response interface. For instance, the command `"completions"` corresponds to the request interface `CompletionsRequest`, and has a response interface defined in `CompletionsResponse`.

# Sample implementations

Expand All @@ -57,15 +57,15 @@ Each command is associated with a request and a response interface. For instance

## Visual Studio Code

[VS Code](https://code.visualstudio.com/)'s [TypeScript support](https://github.com/microsoft/vscode/tree/master/extensions/typescript-language-features) is implemented in TypeScript using `tsserver`.
[VS Code](https://code.visualstudio.com/)'s [TypeScript support](https://github.com/microsoft/vscode/tree/main/extensions/typescript-language-features) is implemented in TypeScript using `tsserver`.

## Tide

[Tide](https://github.com/ananthakumaran/tide) is an elisp implementation for emacs plugin using `tsserver`
[Tide](https://github.com/ananthakumaran/tide) is an Emacs plugin (implemented in elisp) that uses `tsserver`,

## Neovim

[nvim-typescript](https://github.com/mhartington/nvim-typescript) is a neovim plugin using `tsserver`
[nvim-typescript](https://github.com/mhartington/nvim-typescript) is a Neovim plugin using `tsserver`.

# Advanced topics

Expand All @@ -84,53 +84,71 @@ Each command is associated with a request and a response interface. For instance

Note: `file` defaults to `__dirname\.log<PID>` if not specified

**Example**: `set TSS_LOG=-level verbose -file c:\tmp\tsserver.log`
**Example**:
```sh
set TSS_LOG=-level verbose -file c:\tmp\tsserver.log
```

## Cancellation

`tsserver` on startup will try to load module `./cancellationToken` from the containing directory. This module should export a factory function that accepts a list of command line arguments and returns [HostCancellationToken](https://github.com/Microsoft/TypeScript/blob/master/src/services/types.ts#L119-L121). `tsserver` will use this token to check if in-flight operation should be cancelled.
On startup. `tsserver` will try to load the module `./cancellationToken` from the containing directory. This module should export a factory function that accepts a list of command line arguments and returns [HostCancellationToken](https://github.com/Microsoft/TypeScript/blob/main/src/services/types.ts#L254-L256). `tsserver` will use this token to check if in-flight operation should be cancelled.

NOTE: This token will be used for all operations so if one operation is cancelled and cancellation was reported through the token then when another operation is started - token should be reset into the non-cancelled state.
**NOTE:** This token will be used for all operations; so if one operation is cancelled, and cancellation was reported using this the token, then, when another operation is started, the token should be reset to the non-cancelled state.

Default implementation of the cancellation token uses the presence of named pipes as a way to signal cancellation.

1. Before spawning the server, the client generates a unique name. This name is passed to the server as a `cancellationPipeName` command line argument.
2. If some operation on the client side should be cancelled - client opens a named pipe with a name generated on step 1. Nothing needs to be written in the pipe - default cancellation token will interpret the presence of named pipe as a cancellation request.
1. Before spawning the server, the client generates a unique name. This name is passed to the server as a `cancellationPipeName` command line argument.
2. If some operation on the client side should be cancelled, the client opens a named pipe with a name generated on step 1. Nothing needs to be written to the pipe — the default cancellation token will interpret the presence of the named pipe as a cancellation request.
3. After receiving acknowledgment from the server, the client closes the pipe so it can use the same pipe name for the next operation.

Server can split execution of some commands (like `geterr`) in a few steps that will be executed with a delay. This allows it to react on user actions more promptly and not run heavy computations if their results will not be used. However, it introduces a tricky moment in support of cancellations. By allowing request to be suspended and resumed later we break the invariant that was the cornerstone for default implementation of cancellation. Namely now requests can overlap so one pipe name can no longer be used because client have no reason what request is currently executing and will be cancelled. To deal with this issue `tsserver` allows pipe name to be computed dynamically based on current request id. To enable this the client need to provide a value that ends with `*` as the `--cancellationPipeName` argument. If provided cancellation pipe name ends with `*` then default implementation of cancellation token will build expected pipe name as `<cancellationPipeName argument without *><currentRequestId>`. This will allow client to signal any request it thinks is in flight by creating a named pipe with a proper name. Note that server will always send `requestCompleted` message to denote that asynchronous request was completed (either by running to completion or via cancellation) so the client can close named pipe once this message is received.

## Commandline options

Option | Description
------------------------------|-------------
`--cancellationPipeName` | Name of the pipe used as a request cancellation semaphore. See [Cancellation](#cancellation) for more information.
`--syntaxOnly` | A streamlined mode for when the server will only be answering syntactic queries.
`--suppressDiagnosticEvents` | Opt out of receiving events when new diagnostics are discovered (i.e. must request them explicitly).
`--eventPort` | Port used for receiving events. If non is specified events are sent to stdout.
`--useSingleInferredProject` | Put all open .ts and .js files that do not have a .tsconfig file in a common project
`--noGetErrOnBackgroundUpdate`| Opt out of starting `getErr` on `projectsUpdatedInBackground` event
`--locale` | The locale to use to show error messages, e.g. en-us. <br/>Possible values are: <br/>► English (US): `en` <br/>► Czech: `cs` <br/>► German: `de` <br/>► Spanish: `es` <br/>► French: `fr` <br/>► Italian: `it` <br/>► Japanese: `ja` <br/>► Korean: `ko` <br/>► Polish: `pl` <br/>► Portuguese(Brazil): `pt-BR` <br/>► Russian: `ru` <br/>► Turkish: `tr` <br/>► Simplified Chinese: `zh-CN` <br/>► Traditional Chinese: `zh-TW`
Server can split execution of some commands (like `geterr`) in a few steps that will be executed with a delay. This allows it to react on user actions more promptly and not run heavy computations if their results will not be used. However, it introduces a tricky moment in support of cancellations. By allowing requests to be suspended and resumed later we break the invariant that was the cornerstone for the default implementation of cancellation. Namely, requests can now overlap, so one pipe name can no longer be used because the client has no idea what request is currently being executed and will be cancelled. To deal with this issue, `tsserver` allows pipe names to be computed dynamically based on the current request id.
To enable this, the client needs to provide a value that ends with `*` as the `--cancellationPipeName` argument. If the provided cancellation pipe name ends with `*`, then the default implementation of the cancellation token will construct the pipe name as `<cancellationPipeName argument without *><currentRequestId>`. This will allow the client to signal any request it thinks is in flight by creating a named pipe with a proper name. Note that the server will always send a `requestCompleted` message to denote that the asynchronous request was completed (either by running to completion or via cancellation), so the client can close the named pipe once this message is received.

## Command line options

| Option | Description
|--------------------------------|-------------
| `--cancellationPipeName` | Name of the pipe used as a request cancellation semaphore. See [Cancellation](#cancellation) for more information.
| `--syntaxOnly` | A streamlined mode where the server will only be answering syntactic queries.
| `--suppressDiagnosticEvents` | Opt out of receiving events when new diagnostics are discovered (i.e., must request them explicitly).
| `--eventPort` | Port used for receiving events. If none is specified, events are sent to `stdout`.
| `--useSingleInferredProject` | Put all open `.ts` and `.js` files that do not have a `.tsconfig` file into a common project.
| `--noGetErrOnBackgroundUpdate` | Opt out of starting `getErr` on the `projectsUpdatedInBackground` event.
| `--locale` | The locale to use to show error messages, e.g. `en-us`.
| | Possible values are:
| | ► English (US): `en`
| | ► Czech: `cs`
| | ► German: `de`
| | ► Spanish: `es`
| | ► French: `fr`
| | ► Italian: `it`
| | ► Japanese: `ja`
| | ► Korean: `ko`
| | ► Polish: `pl`
| | ► Portuguese (Brazil): `pt-BR`
| | ► Russian: `ru`
| | ► Turkish: `tr`
| | ► Simplified Chinese: `zh-CN`
| | ► Traditional Chinese: `zh-TW`

# Project System

There are three kinds of projects:

## Configured Project

Configured projects are defined by a configuration file, which can be either `tsconfig.json` file or a `jsconfig.json` file.
That configuration file marks the project root path and defines what files to include.
Configured projects are defined by a configuration file, which can be either a `tsconfig.json` file or a `jsconfig.json` file.
That configuration file marks the project root path and defines what files to include.
The configuration file also provide the compiler options to be used for this project.

You can find more information in the [tsconfig.json documentation](http://www.typescriptlang.org/docs/handbook/tsconfig-json.html).
You can find more information in the [tsconfig.json documentation](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html).

## External Project

An external project represents host-specific project formats that TS is not aware of.
An external project represents host-specific project formats that TS is not aware of.
The host is responsible for supplying the list of files in this project and compiler options to use.

Currently VS is the only consumer of this type of project, to model the TS/JS files in a .csproj project.

Currently VS is the only consumer of this type of project, to model the TS/JS files in a `.csproj` project.

## Inferred Project

Expand All @@ -139,19 +157,17 @@ If a file does not have a configuration file (`tsconfig.json` or `jsconfig.json`

The server will include the loose file, then includes all other files included by triple slash references and module imports from the original file transitively.

Compilation options will use the default options for inferred projects.
Compilation options will use the default options for inferred projects.
The host can set the defaults of an inferred project.

## Relationship Among These Projects

In general, the relationship can be summarized as `configured projects > external projects > inferred projects`.

For example, if `file1.ts` belongs to an inferred project, but later a new `tsconfig.json` also includes this file.
Then after the `tsconfig.json` file is found, `file1.ts` will no longer belong to the previous inferred project but the newly created configured project instead.
For example, consider that `file1.ts` belongs to an inferred project, but, later on, a new `tsconfig.json` also includes this file.
Then after the `tsconfig.json` file is found, `file1.ts` will no longer belong to the previous inferred project, but to the newly created configured project instead.
If `file1.ts` used to be the root file of the inferred project, that inferred project will now be destroyed; otherwise it will remain with one fewer file.

For another example, if a `tsconfig.json` file is created to include some files used to belong to an external project (let's call it EP1), then in the current implementation EP1 will be destroyed, all its files either go to the new configured project or will belong to a new inferred project the next time it is opened.
For another example, if a `tsconfig.json` file is created to include some files used to belong to an external project (let's call it EP1), then, since in the current implementation EP1 will be destroyed, all its files either go to the new configured project or will belong to a new inferred project the next time it is opened.

One thing to notice is that one file can belong to multiple projects of the same kind at the same time. E.g., a file can be included by multiple configured projects / inferred projects / external projects.