{"id":13484477,"url":"https://github.com/ocaml/ocaml-lsp","last_synced_at":"2025-05-14T21:06:32.953Z","repository":{"id":37306822,"uuid":"227073289","full_name":"ocaml/ocaml-lsp","owner":"ocaml","description":"OCaml Language Server Protocol implementation","archived":false,"fork":false,"pushed_at":"2025-05-11T00:05:01.000Z","size":7537,"stargazers_count":822,"open_issues_count":156,"forks_count":128,"subscribers_count":19,"default_branch":"master","last_synced_at":"2025-05-11T01:18:29.916Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"OCaml","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ocaml.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2019-12-10T09:03:16.000Z","updated_at":"2025-05-09T15:45:29.000Z","dependencies_parsed_at":"2023-09-26T17:59:17.124Z","dependency_job_id":"c66e9088-4a7b-407d-9567-71258cfefcc9","html_url":"https://github.com/ocaml/ocaml-lsp","commit_stats":{"total_commits":2023,"total_committers":73,"mean_commits":27.71232876712329,"dds":0.2886801779535344,"last_synced_commit":"aae6986391a8519de3da6a7a341f2bd3376e0d2f"},"previous_names":[],"tags_count":60,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ocaml%2Focaml-lsp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ocaml%2Focaml-lsp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ocaml%2Focaml-lsp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ocaml%2Focaml-lsp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ocaml","download_url":"https://codeload.github.com/ocaml/ocaml-lsp/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254227611,"owners_count":22035669,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T17:01:25.010Z","updated_at":"2025-05-14T21:06:27.938Z","avatar_url":"https://github.com/ocaml.png","language":"OCaml","funding_links":[],"categories":["OCaml","Languages","Reason","Developer Tools"],"sub_categories":["Editor Plugins"],"readme":"# OCaml-LSP \u003c!-- omit from toc --\u003e\n\u003c!-- TOC is updated automatically by \"Markdown All in One\" vscode extension --\u003e\n\n[![Build][build-badge]][build]\n[![Coverage Status][coverall-badge]][coverall]\n\n[build-badge]: https://github.com/ocaml/ocaml-lsp/actions/workflows/build-and-test.yml/badge.svg\n[build]: https://github.com/ocaml/ocaml-lsp/actions\n[coverall-badge]: https://coveralls.io/repos/github/ocaml/ocaml-lsp/badge.svg?branch=master\n[coverall]: https://coveralls.io/github/ocaml/ocaml-lsp?branch=master\n\nOCaml-LSP is a language server for OCaml that implements [Language Server\nProtocol](https://microsoft.github.io/language-server-protocol/) (LSP).\n\n\u003e If you use Visual Studio Code, see OCaml Platform extension\n\u003e [page](https://github.com/ocamllabs/vscode-ocaml-platform) for detailed\n\u003e instructions on setting up your editor for OCaml development with OCaml-LSP:\n\u003e what packages need to be installed, how to configure your project and get\n\u003e most out of the OCaml editor support, and how to report and debug problems.\n\n- [Installation](#installation)\n  - [Installing with package managers](#installing-with-package-managers)\n    - [Opam](#opam)\n    - [Esy](#esy)\n  - [Installing from sources](#installing-from-sources)\n  - [Additional package installations](#additional-package-installations)\n- [Usage](#usage)\n  - [Integration with Dune RPC](#integration-with-dune-rpc)\n  - [Merlin configuration (advanced)](#merlin-configuration-advanced)\n- [Features](#features)\n  - [Semantic highlighting](#semantic-highlighting)\n  - [LSP Extensions](#lsp-extensions)\n  - [Unusual features](#unusual-features)\n- [Debugging](#debugging)\n- [Contributing to project](#contributing-to-project)\n  - [Changelog](#changelog)\n- [Tests](#tests)\n- [Relationship to Other Tools](#relationship-to-other-tools)\n- [History](#history)\n- [Comparison to other LSP Servers for OCaml](#comparison-to-other-lsp-servers-for-ocaml)\n\n## Installation\n\nBelow we show how to install OCaml-LSP using opam, esy, and from sources. OCaml-LSP comes in a package called `ocaml-lsp-server` but the installed program (i.e., binary) is called `ocamllsp`.\n\n### Installing with package managers\n\n#### Opam\n\nTo install the language server in the currently used opam [switch](https://opam.ocaml.org/doc/Manual.html#Switches):\n\n```sh\n$ opam install ocaml-lsp-server\n```\n\n_Note:_ you will need to install `ocaml-lsp-server` in every switch where you\nwould like to use it.\n\n#### Esy\n\nTo add the language server to an esy project, run in terminal:\n\n```sh\n$ esy add @opam/ocaml-lsp-server\n```\n\n### Installing from sources\n\nThis project uses submodules to handle dependencies. This is done so that users\nwho install `ocaml-lsp-server` into their sandbox will not share dependency\nconstraints on the same packages that `ocaml-lsp-server` is using.\n\n```sh\n$ git clone --recurse-submodules http://github.com/ocaml/ocaml-lsp.git\n$ cd ocaml-lsp\n$ make install\n```\n\n### Additional package installations\n\n- Install [ocamlformat](https://github.com/ocaml-ppx/ocamlformat#installation)\n  package if you want source file formatting support.\n\n  Note: To have source file formatting support in your project, there needs to\n  be an `.ocamlformat` file present in your project's root directory.\n\n- OCaml-LSP also uses a program called `ocamlformat-rpc` to format code that is\n  either generated or displayed by OCaml-LSP, e.g., when you hover over a module\n  identifier, you can see its typed nicely formatted. This program comes with\n  `ocamlformat` (version \u003e 0.21.0). Previously, it was a standalone package.\n\n## Usage\n\nUsually, your code editor, or some extension/plugin that you install on it, is\nresponsible for launching `ocamllsp`.\n\nImportant: OCaml Language Server has its information about the files from the\nlast time your built your project. We recommend using the Dune build system and\nrunning it in \"watch\" mode to always have correctly functioning OCaml-LSP, e.g.,\n`dune build --watch`.\n\n### Integration with Dune RPC\n\n\u003e since OCaml-LSP 1.11.0\n\nOCaml-LSP can communicate with Dune's RPC system to offer some interesting\nfeatures. User can launch Dune's RPC system by running Dune in watch mode.\nOCaml-LSP will *not* launch Dune's RPC for you. But OCaml-LSP will see if there\nis an RPC running and will communicate with it automatically.\n\nThere are various interesting features and caveats:\n\n1. Dune's RPC enables new kinds of diagnostics (i.e., warnings and errors) to be\n   shown in the editor, e.g., mismatching interface and implementation files.\n   You need to save the file to refresh such diagnostics because Dune doesn't\n   see unsaved files; otherwise, you may see stale (no longer correct) warnings\n   or errors. OCaml-LSP updates diagnostics after each build is complete in\n   watch mode.\n\n2. Dune file promotion support. If you, for example, use `ppx_expect` and have\n   failing tests, you will get a diagnostic when Dune reports that your file can\n   be promoted. You can promote your file using the code action `Promote`.\n\n### Merlin configuration (advanced)\n\nIf you would like OCaml-LSP to respect your `.merlin` files, OCaml-LSP needs to\nbe invoked with `--fallback-read-dot-merlin` argument passed to it and you must\nhave the `dot-merlin-reader` package installed.\n\n## Features\n\n\u003c!-- TODO:\n  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.\n\n  We should consider:\n  1. Moving it to the bottom\n  2. Converting it into a table\n\n     | Description      | Method                  | OCaml | Reason | Dune | Menhir | .ocamlformat | ...\n     | Auto-completion  | textDocument/completion |   x   |    x   |   o  |   o    |      o       | ...\n\n  3. (not sure how) Generate the table automatically because, otherwise, it's outdated frequently.\n--\u003e\n\nThe server supports the following LSP requests (inexhaustive list):\n\n- [x] `textDocument/completion`\n- [x] `completionItem/resolve`\n- [x] `textdocument/hover`\n- [ ] `textDocument/signatureHelp`\n- [x] `textDocument/declaration`\n- [x] `textDocument/definition`\n- [x] `textDocument/typeDefinition`\n- [ ] `textDocument/implementation`\n- [x] `textDocument/codeLens`\n- [x] `textDocument/documentHighlight`\n- [x] `textDocument/documentSymbol`\n- [x] `textDocument/references`\n- [ ] `textDocument/documentColor`\n- [ ] `textDocument/colorPresentation`\n- [x] `textDocument/formatting`\n- [ ] `textDocument/rangeFormatting`\n- [ ] `textDocument/onTypeFormatting`\n- [x] `textDocument/prepareRename`\n- [x] `textDocument/foldingRange`\n- [x] `textDocument/selectionRange`\n- [x] `workspace/didChangeConfiguration`\n- [x] `workspace/symbol`\n\nNote that degrees of support for each LSP request are varying.\n\n## Configuration\n\n[Read more about configurations supported by ocamllsp](./ocaml-lsp-server/docs/ocamllsp/config.md)\n\n### Semantic highlighting\n\n\u003e since OCaml-LSP 1.15.0 (since version `1.15.0-4.14` for OCaml 4, `1.15.0-5.0` for OCaml 5)\n\nSemantic highlighting support is enabled by default.\n\n\u003e since OCaml-LSP 1.14.0\n\nOCaml-LSP implements experimental semantic highlighting support (also known as\nsemantic tokens support). The support can be activated by passing an environment\nvariable to OCaml-LSP:\n\n- To enable non-incremental (expectedly slower but more stable) version, pass\n  `OCAMLLSP_SEMANTIC_HIGHLIGHTING=full` environment variable to OCaml-LSP.\n\n- To enable incremental (potentially faster but more error-prone, at least on VS\n  Code) version, pass `OCAMLLSP_SEMANTIC_HIGHLIGHTING=full/delta` to OCaml-LSP.\n\nTip (for VS Code OCaml Platform users): You can use `ocaml.server.extraEnv`\nsetting in VS Code to pass various environment variables to OCaml-LSP.\n\n```json\n{\n    \"ocaml.server.extraEnv\": {\n        \"OCAMLLSP_SEMANTIC_HIGHLIGHTING\": \"full\"\n    },\n}\n```\n\n### LSP Extensions\n\nThe server also supports a number of OCaml specific extensions to the protocol:\n- [Switch to implementation/interface](ocaml-lsp-server/docs/ocamllsp/switchImplIntf-spec.md)\n- [Infer interface](ocaml-lsp-server/docs/ocamllsp/inferIntf-spec.md)\n- [Locate typed holes](ocaml-lsp-server/docs/ocamllsp/typedHoles-spec.md)\n- [Find wrapping AST node](ocaml-lsp-server/docs/ocamllsp/wrappingAstNode-spec.md)\n\nNote that editor support for these extensions varies. In general, the OCaml Platform extension for Visual Studio Code will have the best support.\n\n### Unusual features\n\n#### Destructing a value \u003c!-- omit in toc --\u003e\n\n\u003e since OCaml-LSP 1.0.0\n\nOCaml-LSP has a code action that allows to generate an exhaustive pattern\nmatching for values. For example, placing a cursor near a value `(Some 10)|`\nwhere `|` is your cursor, OCaml-LSP will offer a code action \"Destruct\", which\nreplaces `(Some 10)` with `(match Some with | None -\u003e _ | Some _ -\u003e _)`.\nImportantly, one can only destruct a value if OCaml-LSP can infer the value's\nprecise type. The value can be type-annotated, e.g., if it's a function argument\nwith polymorphic (or yet unknown) type in this context. In the code snippet\nbelow, we type-annotate the function parameter `v` because when we type `let f v\n= v|`, the type of `v` is polymorphic, so we can't destruct it.\n\nYou can also usually destruct the value by placing the cursor on the wildcard\n(`_`) pattern in a pattern-match. For example,\n\n```ocaml\ntype t = A | B of string option\n\nlet f (v : t) = match v with | A -\u003e _ | B _| -\u003e _\n```\n\ninvoking destruct near the cursor (`|`) in the snippet above, you get\n\n```ocaml\ntype t = A | B of string option\n\nlet f (v : t) = match v with | A -\u003e _ | B (None) | B (Some _) -\u003e _\n```\n\nImportantly, note the underscores in place of expressions in each branch of the\npattern match above. The underscores that occur in place of expressions are\ncalled \"typed holes\" - a concept explained below.\n\nTip (formatting): generated code may not be greatly formatted. If your project\nuses a formatter such as OCamlFormat, you can run formatting and get a\nwell-formatted document (OCamlFormat supports typed holes formatting).\n\nTip (for VS Code OCaml Platform users): You can destruct a value using a keybinding\n\u003ckbd\u003eAlt\u003c/kbd\u003e+\u003ckbd\u003eD\u003c/kbd\u003e or on MacOS \u003ckbd\u003eOption\u003c/kbd\u003e+\u003ckbd\u003eD\u003c/kbd\u003e\n\n#### Typed holes \u003c!-- omit in toc --\u003e\n\n\u003e since OCaml-LSP 1.8.0\n\nOCaml-LSP has a concept of a \"typed hole\" syntactically represented as `_`\n(underscore). A typed hole represents a well-typed \"substitute\" for an\nexpression. OCaml-LSP considers these underscores that occur in place of\nexpressions as a valid well-typed OCaml program: `let foo : int = _` (the typed\nhole has type `int` here) or `let bar = _ 10` (the hole has type `int -\u003e 'a`).\nOne can use such holes during development as temporary substitutes for\nexpressions and \"plug\" the holes later with appropriate expressions.\n\nNote, files that incorporate typed holes are *not* considered valid OCaml by the\nOCaml compiler and, hence, cannot be compiled.\n\nAlso, an underscore occurring in place of a pattern (for example `let _ = 10`)\nshould not be confused with a typed hole that occurs in place of an expression,\ne.g., `let a = _`.\n\n#### Constructing values by type (experimental) \u003c!-- omit in toc --\u003e\n\n\u003e since OCaml-LSP 1.8.0\n\nOCaml-LSP can \"construct\" expressions based on the type required and offer them\nduring auto-completion. For example, typing `_` (typed hole) in the snippet\nbelow will trigger auto-completion (`|` is your cursor):\n\n```ocaml\n(* file foo.ml *)\ntype t = A | B of string option\n\n(* file bar.ml *)\nlet v : Foo.t = _|\n```\n\nThe auto-completion offers completions `Foo.A` and `Foo.B _`. You can further\nconstruct values by placing the cursor as such: `Foo.B _|` and triggering code\naction \"Construct an expression\" which offers completions `None` and `Some _`.\nTrigger the same code action in `Some _|` will offer `\"\"` - one of the possible\nexpressions to replace the typed hole with.\n\nConstructing a value is thus triggered either by typing `_` in place of an\nexpression or trigger the code action \"Construct an Expression\". Also, the type\nof the value needs to be non-polymorphic to construct a meaningful value.\n\nTip (for VS Code OCaml Platform users): You can construct a value using a keybinding\n\u003ckbd\u003eAlt\u003c/kbd\u003e+\u003ckbd\u003eC\u003c/kbd\u003e or on MacOS \u003ckbd\u003eOption\u003c/kbd\u003e+\u003ckbd\u003eC\u003c/kbd\u003e\n\n#### Syntax Documentation\n\n\u003e since OCaml-LSP 1.18.0\n\nOCaml-LSP can display documentation about the node under the cursor when\nthe user hovers over some OCaml code. For example, hovering over the code\nsnippet below will display some information about what the syntax\nis:\n\n```ocaml\ntype point = {x: int; y: int}\n```\nHovering over the above will\ndisplay:\n```\nocaml type point = { x : int; y : int }\nsyntax Record type:\nAllows you to define variants with a fixed set of fields, and all of the\nconstructors for a record variant type must have the same fields. See\nManual\n```\nThe documentation is gotten from the Merlin engine which receives\nthe nodes under the cursor and infers what the syntax may be about, and\ndisplays the required information along with links to the manual for further\nreading.\n\nSyntax Documentation is an optional feature and can be activated by\nusing the LSP config system with the key called `syntaxDocumentation` and can\nbe enabled via setting it to `{ enable: true }`.\n\n## Debugging\n\nIf you use Visual Studio Code, please see OCaml Platform extension\n[page](https://github.com/ocamllabs/vscode-ocaml-platform) for a detailed guide\non how to report and debug problems.\n\nIf you use another code editor and use OCaml-LSP, you should be able to set the\nserver trace to `verbose` using your editor's LSP client and watch the trace\nfor errors such as logged exceptions.\n\n## Contributing to project\n\n```bash\n# clone repo with submodules\ngit clone --recursive git@github.com:ocaml/ocaml-lsp.git\n\ncd ocaml-lsp\n\n# if you already cloned, pull submodules\ngit submodule update --init --recursive\n\n# create local switch (or use global one)\nopam switch --yes create .\n\n# don't forget to set your environment to use the local switch\neval $(opam env)\n\n# install dependencies\nmake install-test-deps\n\n# build\nmake all\n\n# the ocamllsp executable can be found at _build/default/ocaml-lsp-server/bin/main.exe\n```\n\n### Changelog\n\nUser-visible changes should come with an entry in the changelog under the appropriate part of\nthe **unreleased** section. PR that doesn't provide an entry will fail CI check. This behavior\ncan be overridden by using the \"no changelog\" label, which is used for changes that are not user-visible.\n\n## Tests\n\nTo run tests execute:\n\n```sh\n$ make test\n```\n\nNote that tests require [Node.js](https://nodejs.org/en/) and\n[Yarn](https://yarnpkg.com/lang/en/) installed.\n\n## Relationship to Other Tools\n\nThe lsp server uses merlin under the hood, but users are not required to have\nmerlin installed. We vendor merlin because we currently heavily depend on some\nimplementation details of merlin that make it infeasible to upgrade the lsp\nserver and merlin independently.\n\n## History\n\nThe implementation of the lsp protocol itself was taken from\n[facebook's hack](https://github.com/facebook/hhvm/blob/master/hphp/hack/src/utils/lsp/lsp.mli)\n\nPreviously, this lsp server was a part of merlin, until it was realized that\nthe lsp protocol covers a wider scope than merlin.\n\n## Comparison to other LSP Servers for OCaml\n\nNote that the comparisons below make no claims of being objective and may be\nentirely out of\ndate. Also, both servers seem deprecated.\n\n- [reason-language-server](https://github.com/jaredly/reason-language-server)\n  This server supports\n  [bucklescript](https://github.com/BuckleScript/bucklescript) \u0026\n  [reason](https://github.com/facebook/reason). However, this project does not\n  use merlin which means that it supports fewer versions of OCaml and offers less\n  \"smart\" functionality - especially in the face of sources that do not yet\n  compile.\n\n- [ocaml-language-server](https://github.com/ocaml-lsp/ocaml-language-server)\n  This project is extremely similar in the functionality it provides because it\n  also reuses merlin on the backend. The essential difference is that this\n  project is written in typescript, while our server is in OCaml. We feel that\n  it's best to use OCaml to maximize the contributor pool.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Focaml%2Focaml-lsp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Focaml%2Focaml-lsp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Focaml%2Focaml-lsp/lists"}