# \[ANN\] ocaml-lsp preview

**URL:** <https://discuss.ocaml.org/t/ann-ocaml-lsp-preview/4876>\
**Category:** Community\
**Tags:** announce\
**Created:** [December 17, 2019, 3:13pm UTC](https://discuss.ocaml.org/t/ann-ocaml-lsp-preview/4876 "2019-12-17T15:13:30Z")\
**Posts on this page:** 16\
**Page:** 1

<div class="post-metadata">

**Author:** ![rgrinberg](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/rgrinberg/32/40_2.png) [@rgrinberg](https://discuss.ocaml.org/u/rgrinberg)\
**Post date:** [December 17, 2019, 3:13pm UTC](https://discuss.ocaml.org/t/ann-ocaml-lsp-preview/4876/1 "2019-12-17T15:13:30Z")

</div>

Dear OCaml Hackers,

I’m excited to announce [ocaml-lsp](https://github.com/ocaml/ocaml-lsp). This project contains an implementation of an LSP server for the OCaml language. The current implementation piggy backs on the widely successful [merlin](https://github.com/ocaml/merlin) tool to provide completion & type inference. In the future, we’d like to use all other essential tools such as ocamlformat, odoc, dune to provide more functionality in your editors.

For now, the project isn’t yet available on opam as we’re still polishing some rough edges in the release process. Nevertheless, I invite all brave souls who are ready to experiment to give this lsp server a try. Your feedback & contributions are most welcome 🙂

---

<div class="post-metadata">

**Author:** ![UnixJunkie](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/unixjunkie/32/638_2.png) [@UnixJunkie](https://discuss.ocaml.org/u/UnixJunkie)\
**Post date:** [December 18, 2019, 4:55am UTC](https://discuss.ocaml.org/t/ann-ocaml-lsp-preview/4876/2 "2019-12-18T04:55:18Z")

</div>

Hello,

This project looks nice.

If I am an Emacs or Vi user, can I take advantage of an LSP server?

Or, is this only for some new editors like Atom or VScode?

Thanks,  
F.

---

<div class="post-metadata">

**Author:** ![XVilka](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/xvilka/32/5334_2.png) [@XVilka](https://discuss.ocaml.org/u/XVilka)\
**Post date:** [December 18, 2019, 6:51am UTC](https://discuss.ocaml.org/t/ann-ocaml-lsp-preview/4876/3 "2019-12-18T06:51:44Z")

</div>

@UnixJunkie of course! That’s the whole point of this tooling.

For Vim you can choose between:

- [Coc.nvim](https://github.com/neoclide/coc.nvim) - most powerful of all, but written in TypeScript and heaviest of all
- [Ale](https://github.com/dense-analysis/ale) - pure VimL
- [vim-lsp](https://github.com/prabirshrestha/vim-lsp) - pure VimL
- [LanguageClient-neovim](https://github.com/autozimu/LanguageClient-neovim) - written in Rust
- Some other implementations

I am not an Emacs expert, but there is amazing LSP integration too:

- [lsp-mode](https://github.com/emacs-lsp/lsp-mode)

 ![image](https://us1.discourse-cdn.com/flex020/uploads/ocaml/original/2X/b/b8acd745527e801fef1eb3d4e8722d49c5c2ed1a.png)

---

<div class="post-metadata">

**Author:** ![psafont](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/psafont/32/4089_2.png) [@psafont](https://discuss.ocaml.org/u/psafont)\
**Post date:** [December 18, 2019, 1:37pm UTC](https://discuss.ocaml.org/t/ann-ocaml-lsp-preview/4876/4 "2019-12-18T13:37:18Z")

</div>

Neovim 0.5.0 (now pre-released) has native LSP support as well: [https://github.com/neovim/neovim/pull/11336](https://github.com/neovim/neovim/pull/11336)

Not sure how well integrated is it going to be with various plugins ([example](https://github.com/Shougo/deoplete-lsp))

---

<div class="post-metadata">

**Author:** ![zozozo](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/zozozo/32/59_2.png) [@zozozo](https://discuss.ocaml.org/u/zozozo)\
**Post date:** [December 18, 2019, 1:45pm UTC](https://discuss.ocaml.org/t/ann-ocaml-lsp-preview/4876/5 "2019-12-18T13:45:28Z")

</div>

> [@rgrinberg](#):
>
> we’d like to use all other essential tools such as ocamlformat, odoc, dune to provide more functionality

What about `ocp-indent` ?

---

<div class="post-metadata">

**Author:** ![rgrinberg](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/rgrinberg/32/40_2.png) [@rgrinberg](https://discuss.ocaml.org/u/rgrinberg)\
**Post date:** [December 18, 2019, 2:09pm UTC](https://discuss.ocaml.org/t/ann-ocaml-lsp-preview/4876/6 "2019-12-18T14:09:01Z")

</div>

It’s not on the roadmap but I don’t see why support can’t be added. Feel free to add support for it if this is a feature you’d like.

---

<div class="post-metadata">

**Author:** ![c-cube](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/c-cube/32/1727_2.png) [@c-cube](https://discuss.ocaml.org/u/c-cube)\
**Post date:** [December 18, 2019, 2:38pm UTC](https://discuss.ocaml.org/t/ann-ocaml-lsp-preview/4876/7 "2019-12-18T14:38:24Z")

</div>

I imagine the current plugins for ocp-indent would still work, and personally that should be enough for me.

---

<div class="post-metadata">

**Author:** ![UnixJunkie](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/unixjunkie/32/638_2.png) [@UnixJunkie](https://discuss.ocaml.org/u/UnixJunkie)\
**Post date:** [December 19, 2019, 3:13am UTC](https://discuss.ocaml.org/t/ann-ocaml-lsp-preview/4876/8 "2019-12-19T03:13:33Z")

</div>

Is there any relation with that:

> **[ocaml-lsp/ocaml-language-server](https://github.com/ocaml-lsp/ocaml-language-server)**
>
> Language server for OCaml and Reason. Contribute to ocaml-lsp/ocaml-language-server development by creating an account on GitHub.

Does it need it?

---

<div class="post-metadata">

**Author:** ![hongchangwu](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/hongchangwu/32/1146_2.png) [@hongchangwu](https://discuss.ocaml.org/u/hongchangwu)\
**Post date:** [December 19, 2019, 4:55pm UTC](https://discuss.ocaml.org/t/ann-ocaml-lsp-preview/4876/9 "2019-12-19T16:55:08Z")

</div>

Great news. I’m a little concerned though that `ocaml-lsp` vendors `merlin`. If they are indeed tightly coupled, shouldn’t they still be developed and released together? Or is there plan to de-couple them in the future?

---

<div class="post-metadata">

**Author:** ![rgrinberg](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/rgrinberg/32/40_2.png) [@rgrinberg](https://discuss.ocaml.org/u/rgrinberg)\
**Post date:** [December 19, 2019, 5:57pm UTC](https://discuss.ocaml.org/t/ann-ocaml-lsp-preview/4876/10 "2019-12-19T17:57:08Z")

</div>

We might decouple them further in the future but it’s not a huge concern at the moment. The issue with keeping the server inside merlin was that merlin is one of the few tools that we’d like to integrate with.

I understand the concern the concern that it’s more convenient the develop them together, but really that’s a wider concern at the level of the whole OCaml platform. This deserves it’s own discussion, but I personally think that solving this will require new package management tooling.

---

<div class="post-metadata">

**Author:** ![XVilka](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/xvilka/32/5334_2.png) [@XVilka](https://discuss.ocaml.org/u/XVilka)\
**Post date:** [December 19, 2019, 11:51pm UTC](https://discuss.ocaml.org/t/ann-ocaml-lsp-preview/4876/11 "2019-12-19T23:51:27Z")

</div>

NeoVim 0.5.0 will also include the [tree-sitter](https://tree-sitter.github.io/tree-sitter/) parser for syntax highlighting, which will allow way better coloring. And tree-sitter already has [OCaml grammar](https://github.com/tree-sitter/tree-sitter-ocaml), so implementing semantics-aware syntax highlighter will be easier. But I expect the support more or less ready for external contributions only in 0.6.0, sadly. Integrating the tool with something like [GitHub Semantic](https://github.com/github/semantic) (_Haskell alert_) will greatly improve OCaml experience on GitHub too, see the [corresponding issue](https://github.com/github/semantic/issues/138).

---

<div class="post-metadata">

**Author:** ![bluddy](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/bluddy/32/104_2.png) [@bluddy](https://discuss.ocaml.org/u/bluddy)\
**Post date:** [December 20, 2019, 12:10am UTC](https://discuss.ocaml.org/t/ann-ocaml-lsp-preview/4876/12 "2019-12-20T00:10:00Z")

</div>

Wow I didn’t realize that’s what tree-sitter was. That’s awesome.

---

<div class="post-metadata">

**Author:** ![314eter](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/314eter/32/25_2.png) [@314eter](https://discuss.ocaml.org/u/314eter)\
**Post date:** [December 20, 2019, 1:25am UTC](https://discuss.ocaml.org/t/ann-ocaml-lsp-preview/4876/13 "2019-12-20T01:25:31Z")

</div>

The next step for Semantic support is documented [here](https://github.com/tree-sitter/haskell-tree-sitter/blob/master/docs/codegen.md), but I’m working on some [improvements](https://github.com/tree-sitter/tree-sitter-ocaml/pull/36) of the tree-sitter parser first.

---

<div class="post-metadata">

**Author:** ![carlosdagos](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/carlosdagos/32/1275_2.png) [@carlosdagos](https://discuss.ocaml.org/u/carlosdagos)\
**Post date:** [December 30, 2019, 7:34am UTC](https://discuss.ocaml.org/t/ann-ocaml-lsp-preview/4876/14 "2019-12-30T07:34:06Z")

</div>

For Emacs there is also `eglot`: [https://github.com/joaotavora/eglot](https://github.com/joaotavora/eglot) – As the README says, it’s quite minimalist compared to `lsp-mode`.

---

<div class="post-metadata">

**Author:** ![edwin](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/edwin/32/627_2.png) [@edwin](https://discuss.ocaml.org/u/edwin)\
**Post date:** [December 31, 2019, 5:09pm UTC](https://discuss.ocaml.org/t/ann-ocaml-lsp-preview/4876/15 "2019-12-31T17:09:48Z")

</div>

> [@XVilka](#):
>
> Ale

Here is an example with ALE and Neovim (tested with v0.3.8):

- Install the [Ale](https://github.com/dense-analysis/ale) plugin. If your Vim has support for packages (Vim 8+ or Neovim) you can simply clone it in the correct subdir, no need for a plugin manager:  
`git clone https://github.com/w0rp/ale.git .vim/pack/my-plugins/start/ale`
- Add this to your .vimrc:

```plaintext
" only invoke merlin to check for errors when                                                                                                                               
" exiting insert mode, not on each keystroke.                                                                                                                               
let g:ale_lint_on_text_changed="never"                                                                                                                                      
let g:ale_lint_on_insert_leave=1                                                                                                                                            
                                                                                                                                                                            
" enable ALE's internal completion if deoplete is not used                                                                                                                  
let g:ale_completion_enabled=1                                                                                                                                              
                                                                                                                                                                            
" only pop up completion when stopped typing for ~0.5s,                                                                                                                     
" to avoid distracting when completion is not needed                                                                                                                        
let g:ale_completion_delay=500                                                                                                                                              
                                                                                                                                                                            
" see ale-completion-completeopt-bug                                                                                                                                        
set completeopt=menu,menuone,preview,noselect,noinsert                                                                                                                      
                                                                                                                                                                            
if has('packages')                                                                                                                                                          
    packloadall                                                                                                                                                             
                                                                                                                                                                            
    " This should be part of ALE itself, like ols.vim                                                                                                                       
    call ale#linter#Define('ocaml',{                                                                                                                                        
                \ 'name':'ocaml-lsp',                                                                                                                                       
                \ 'lsp': 'stdio',                                                                                                                                           
                \ 'executable': 'ocamllsp',                                                                                                                                 
                \ 'command': '%e',                                                                                                                                          
                \ 'project_root': function('ale#handlers#ols#GetProjectRoot')                                                                                               
                \})                                                                                                                                                         
                                                                                                                                                                            
    " remap 'gd' like Merlin would                                                                                                                                          
    nmap <silent><buffer> gd <Plug>(ale_go_to_definition_in_split)<CR>                                                                                                     
                                                                                                                                                                            
    " go back                                                                                                                                                               
    nnoremap <silent> <LocalLeader>gb <C-O>                                                                                                                                 
                                                                                                                                                                            
    " show list of file:line:col of references for symbol under cursor                                                                                                      
    nmap <silent><buffer> <LocalLeader>go :ALEFindReferences -relative<CR>                                                                                                  
                                                                                                                                                                            
    " Show documentation if available, and type                                                                                                                             
    nmap <silent><buffer> <LocalLeader>hh <Plug>(ale_hover)<CR>                                                                                                             
                                                                                                                                                                            
    " So I can type ,hh. More convenient than \hh.                                                                                                                          
    nmap , <LocalLeader>                                                                                                                                                    
    vmap , <LocalLeader>                                                                                                                                                    
endif 

```

---

<div class="post-metadata">

**Author:** ![Andy123](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/andy123/32/2068_2.png) [@Andy123](https://discuss.ocaml.org/u/Andy123)\
**Post date:** [May 23, 2020, 12:17pm UTC](https://discuss.ocaml.org/t/ann-ocaml-lsp-preview/4876/16 "2020-05-23T12:17:29Z")

</div>

Hi, Did you get this working when using Jane Street’s Base module.  
I’m seeing warnings here. I’m using ALE in neovim. ocam 4.10, all new setup

```
open Base <<Unbound Module Base

```

let downcase\_extension filename =  
match String.rsplit2 filename ~on:’.’ with \<\< Unbound value String.rsplit2  
| None -\> filename  
| Some (base,ext) -\>  
base ^ “.” ^ String.lowercase ext

(_call like this map downcase\_extension [“ABC.TXT”;“abc.Txt”];;_)

let map f l =  
List.map ~f:f l \<\< does not like ~f

type point2d = {x:float;y:float}

let magnitude {x=x\_pos; y=y\_pos} =  
Float.sqrt(x\_pos \*\*. 2. +. y\_pos \*\*. 2.) \<\< does not like \*\*
