Skip to content

Commit

Permalink
Update readme (#895)
Browse files Browse the repository at this point in the history
* readme: mention lsp server alternatives are deprecated

* readme: document destruct, construct, and typed holes

* docs: improve formatting of custom requests

* readme: TODO regarding ofmt-rpc package

* readme: specify which vscode extension the tips apply to

* readme: mention existence of semantic highlighting support behind a feature flag

* readme: mention existence of `--fallback-read-dot-merlin`

* readme: mention about type-annotations for construct/destruct
  • Loading branch information
ulugbekna authored Oct 24, 2022
1 parent 8ee39df commit 2cca06f
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 19 deletions.
138 changes: 134 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,26 @@ For an example of usage of the server in a VS Code extension, see OCaml
Platform Extension implementation
[here](https://github.com/ocamllabs/vscode-ocaml-platform/blob/master/src/vscode_ocaml_platform.ml).

### Merlin configuration (advanced)

If you would like OCaml-LSP to respect your `.merlin` files, OCaml-LSP needs to
be invoked with `--fallback-read-dot-merlin` argument passed to it.

## Features

<!-- TODO:
this is quite a large list (which becomes even larger since it's missing some requests), which is not necessarily of big interest to users.
We should consider:
1. Moving it to the bottom
2. Converting it into a table
| Description | Method | OCaml | Reason | Dune | Menhir | .ocamlformat | ...
| Auto-completion | textDocument/completion | x | x | o | o | o | ...
3. (not sure how) Generate the table automatically because, otherwise, it's outdated frequently.
-->

The server supports the following LSP requests:

- [x] `textDocument/completion`
Expand All @@ -85,14 +103,125 @@ The server supports the following LSP requests:

Note that degrees of support for each LSP request are varying.

### Semantic highlighting

OCaml-LSP implements experimental semantic highlighting support (also known as
semantic tokens support). The support can be activated by passing an evironment
variable to OCaml-LSP:

- To enable non-incremental (expectedly slower but more stable) version, pass
`OCAMLLSP_SEMANTIC_HIGHLIGHTING=full` environment variable to OCaml-LSP.

- To enable incremental (potentially faster but more error-prone, at least on VS
Code) version, pass `OCAMLLSP_SEMANTIC_HIGHLIGHTING=full/delta` to OCaml-LSP.

Tip (for VS Code OCaml Platform users): You can use `ocaml.server.extraEnv`
setting in VS Code to pass various environment variables to OCaml-LSP.

```json
{
"ocaml.server.extraEnv": {
"OCAMLLSP_SEMANTIC_HIGHLIGHTING": "full"
},
}
```

### LSP Extensions

The server also supports a number of OCaml specific extensions to the protocol:
- [Infer interface](ocaml-lsp-server/docs/ocamllsp/inferIntf-spec.md)
- [Switch to implementation/interface](ocaml-lsp-server/docs/ocamllsp/switchImplIntf-spec.md)
- [Infer interface](ocaml-lsp-server/docs/ocamllsp/inferIntf-spec.md)
- [Locate typed holes](ocaml-lsp-server/docs/ocamllsp/typedHoles-spec.md)
- [Find wrapping AST node](ocaml-lsp-server/docs/ocamllsp/wrappingAstNode-spec.md)

Note that editor support for these extensions varies. In general, the OCaml Platform extension for Visual Studio Code will have the best support.

### Unorthodox features

#### Destructing a value

OCaml-LSP has a code action that allows to generate an exhaustive pattern
matching for values. For example, placing a cursor near a value `(Some 10)|`
where `|` is your cursor, OCaml-LSP will offer a code action "Destruct", which
replaces `(Some 10)` with `(match Some with | None -> _ | Some _ -> _)`.
Importantly, one can only destruct a value if OCaml-LSP can infer the value's
precise type. The value can be type-annotated, e.g., if it's a function argument
with polymorphic (or yet unknown) type in this context. In the code snippet
below, we type-annotate the function parameter `v` because when we type `let f v
= v|`, the type of `v` is polymorphic, so we can't destruct it.

You can also usually destruct the value by placing the cursor on the wildcard
(`_`) pattern in a pattern-match. For example,

```ocaml
type t = A | B of string option
let f (v : t) = match v with | A -> _ | B _| -> _
```

invoking destruct near the cursor (`|`) in the snippet above, you get

```ocaml
type t = A | B of string option
let f (v : t) = match v with | A -> _ | B (None) | B (Some _) -> _
```

Importantly, note the undescores in place of expressions in each branch of the
pattern match above. The underscores that occur in place of expressions are
called "typed holes" - a concept explained below.

Tip (formatting): generated code may not be greatly formatted. If your project
uses a formatter such as OCamlFormat, you can run formatting and get a
well-formatted document (OCamlFormat supports typed holes formatting).

Tip (for VS Code OCaml Platform users): You can destruct a value using a keybinding
<kbd>Alt</kbd>+<kbd>D</kbd> or on MacOS <kbd>Option</kbd>+<kbd>D</kbd>

#### Typed holes

OCaml-LSP has a concept of a "typed hole" syntactically represented as `_`
(underscore). A typed hole represents a well-typed "substitute" for an
expression. OCaml-LSP considers these underscores that occur in place of
expressions as a valid well-typed OCaml program: `let foo : int = _` (the typed
hole has type `int` here) or `let bar = _ 10` (the hole has type `int -> 'a`).
One can use such holes during development as temporary substitutes for
expressions and "plug" the holes later with appropriate expressions.

Note, files that incorporate typed holes are *not* considered valid OCaml by the
OCaml compiler and, hence, cannot be compiled.

Also, an underscore occurring in place of a pattern (for example `let _ = 10`)
should not be confused with a typed hole that occurs in place of an expression,
e.g., `let a = _`.

#### Constructing values by type (experimental)

OCaml-LSP can "construct" expressions based on the type required and offer them
during auto-completion. For example, typing `_` (typed hole) in the snippet
below will trigger auto-completion (`|` is your cursor):

```ocaml
(* file foo.ml *)
type t = A | B of string option
(* file bar.ml *)
let v : Foo.t = _|
```

The auto-completion offers completions `Foo.A` and `Foo.B _`. You can further
construct values by placing the cursor as such: `Foo.B _|` and triggering code
action "Construct an expression" which offers completions `None` and `Some _`.
Trigger the same code action in `Some _|` will offer `""` - one of the possible
expressions to replace the typed hole with.

Constructing a value is thus triggered either by typing `_` in place of an
expression or trigger the code action "Construct an Expression". Also, the type
of the value needs to be non-polymorphic to construct a meaningful value.

Tip (for VS Code OCaml Platform users): You can construct a value using a keybinding
<kbd>Alt</kbd>+<kbd>C</kbd> or on MacOS <kbd>Option</kbd>+<kbd>C</kbd>

## Integration with other tools

### Source file formatting: OCamlFormat & Refmt
Expand All @@ -104,7 +233,7 @@ formatting support. Note, however, that OCaml-LSP requires presence of
OCamlFormat configuration file, called `.ocamlformat`, in the project root to
be able to format source files in your project.

### Formatting code on hover
### Formatting code on hover <!-- TODO: specify until which olsp version this applies (since ofmt-rpc is inside ofmt now) -->

When you hover the cursor over OCaml code, the extension shows you the type of
the symbol. To get nicely formatted types, install
Expand Down Expand Up @@ -174,8 +303,9 @@ the lsp protocol covers a wider scope than merlin.

## Comparison to other LSP Servers for OCaml

Note that the comparisons below makes no claims of being objective and may be
entirely out of date:
Note that the comparisons below make no claims of being objective and may be
entirely out of
date. Also, both servers seem deprecated.

- [reason-language-server](https://github.com/jaredly/reason-language-server)
This server supports
Expand Down
10 changes: 5 additions & 5 deletions ocaml-lsp-server/docs/ocamllsp/inferIntf-spec.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#### Infer Interface Request
# Infer Interface Request

Infer Interface Request is sent from the client to the server to get the infered
interface for a given module implementation.
Expand All @@ -10,21 +10,21 @@ If the file cannot be found in the document store, an error will be returned.
Warning: this custom request is meant to be consumed by `ocaml-vscode-platform` exclusively,
it can be removed any time and should not be relied on.

##### Client capability
## Client capability

nothing that should be noted

##### Server capability
## Server capability

property name: `handleInferIntf`
property type: `boolean`

##### Request
## Request

- method: `ocamllsp/inferIntf`
- params: `DocumentUri` (see [`DocumentUri`](https://microsoft.github.io/language-server-protocol/specifications/specification-current/#uri) in LSP specification)

##### Response
## Response

- result: String
- error: code and message set in case an exception happens during the processing of the request.
10 changes: 5 additions & 5 deletions ocaml-lsp-server/docs/ocamllsp/switchImplIntf-spec.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#### Switch Implementation/Interface Request
# Switch Implementation/Interface Request

Switch Implementation/Interface Request is sent from client to server to get
URI(s) of the file(s) that the current file can switch to, e.g., if the user
Expand All @@ -12,22 +12,22 @@ for creation is returned, e.g., if a user wants to switch from "foo.ml", but no
files already exist in the project that could be returned, a URI for "foo.mli"
is returned.

##### Client capability
## Client capability

nothing that should be noted

##### Server capability
## Server capability

property name: `handleSwitchImplIntf`

property type: `boolean`

##### Request
## Request

- method: `ocamllsp/switchImplIntf`
- params: `DocumentUri` (see [`DocumentUri`](https://microsoft.github.io/language-server-protocol/specifications/specification-current/#uri) in LSP specification)

##### Response
## Response

- result: DocumentUri[] (non-empty)
- error: code and message set in case an exception happens during the `ocamllsp/switchImplIntf` request.
Expand Down
12 changes: 7 additions & 5 deletions ocaml-lsp-server/docs/ocamllsp/wrappingAstNode-spec.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#### Wrapping AST Node
# Wrapping AST Node

## Description

(Could also be named `Enclosing AST Node`)

Expand Down Expand Up @@ -30,16 +32,16 @@ The document URI in the request has to be open before sending a the request. If

Note: stability of this custom request is not guaranteed. Talk to the maintainers if you want to depend on it.

##### Client capability
## Client capability

nothing that should be noted

##### Server capability
## Server capability

property name: `handleWrappingAstNode`
property type: `boolean`

##### Request
## Request

- method: `ocamllsp/wrappingAstNode`
- params:
Expand All @@ -51,7 +53,7 @@ property type: `boolean`
}
```

##### Response
## Response

- result: `Range | null`
- error: code and message set in case an exception happens during the processing of the request.

0 comments on commit 2cca06f

Please sign in to comment.