Skip to content

Instantly share code, notes, and snippets.

@wokalski
Last active February 21, 2020 12:44
Show Gist options
  • Save wokalski/5ab883e433c91752b28a72688cb398ac to your computer and use it in GitHub Desktop.
Save wokalski/5ab883e433c91752b28a72688cb398ac to your computer and use it in GitHub Desktop.
BuckleScript friendly `vim` + official ocaml-lsp setup for Reason/OCaml

BuckleScript friendly vim + official ocaml-lsp setup for Reason/OCaml

Prerequisite: Install esy 0.6.0.

Steps

  1. Check esy version (it has to be >0.6.0)
  2. Create esy.json with the following contents next to package.json:
{
  "dependencies": { "ocaml": "4.6.x" },
  "devDependencies": {
    "@opam/ocaml-lsp-server": "ocaml/ocaml-lsp:ocaml-lsp-server.opam#4cfc07d",
    "@opam/reason": "*"
  },
  "resolutions": {
    "@opam/reason": "facebook/reason#b555665"
  }
}
  1. Run esy in that directory
  2. Configure your vim LSP client accordingly. ALE example:
let g:ale_fixers = {
\   'ocaml': ['ocamlformat'
\}

let g:ale_linters = {
\   'reason': ['ocaml-lsp'],
\   'ocaml': ['ocaml-lsp'],
\}

" OCaml/Reason

au BufRead,BufNewFile *.re set filetype=reason
au BufRead,BufNewFile *.rei set filetype=reason

function! s:fix_refmt(buffer) abort
  let ext = expand('#' . a:buffer . ':e')
  if ext ==# 'rei'
    return {
    \   'command': 'esy refmt --interface true'
    \}
  else
    return {
    \   'command': 'esy refmt'
    \}
  endif
endfunction

let g:ale_fixers.reason = [function('s:fix_refmt')]

function! s:executable_callback(buffer) abort
  return 'true'
endfunction

function! s:get_command(buffer) abort
  return 'esy exec-command --include-current-env ocamllsp'
endfunction

function! s:get_language(buffer) abort
  return getbufvar(a:buffer, '&filetype')
endfunction

function! s:get_project_root(buffer) abort
  let l:merlin_file = ale#path#FindNearestFile(a:buffer, 'esy.json')
  if empty(l:merlin_file)
    let l:merlin_file = ale#path#FindNearestFile(a:buffer, 'package.json')
  endif
  return !empty(l:merlin_file) ? fnamemodify(l:merlin_file, ':h') : ''
endfunction

call ale#linter#Define('ocaml', {
\   'name': 'ocaml-lsp',
\   'lsp': 'stdio',
\   'executable': function('s:executable_callback'),
\   'command': function('s:get_command'),
\   'language': function('s:get_language'),
\   'project_root': function('s:get_project_root')
\})

call ale#linter#Define('reason', {
\   'name': 'ocaml-lsp',
\   'lsp': 'stdio',
\   'executable': function('s:executable_callback'),
\   'command': function('s:get_command'),
\   'language': function('s:get_language'),
\   'project_root': function('s:get_project_root')
\})

Why ocaml-lsp?

ocaml-lsp is the official LSP server for OCaml and Reason. It's well maintained and based on the state of the art OCaml/Reason editor intelligence engine - merlin.

@wokalski
Copy link
Author

wokalski commented Feb 6, 2020

Troubleshooting

Error: Library "merlin.specific" not found

Make sure you have esy 0.6.0 or newer. If you attempted an install with earlier version, remove _esy and esy.lock.

@wokalski
Copy link
Author

wokalski commented Feb 6, 2020

Apparently there's a temporary issue with ocaml-lsp + ocaml 4.6. Some dependency might have been forced pushed to at some point and it broke the build.

ocaml/ocaml-lsp#82

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment