Migrating from tuareg / caml-mode¶
If you're switching to neocaml from tuareg or caml-mode, here's what you need to know.
File associations¶
neocaml automatically registers .ml and .mli files via auto-mode-alist --
you don't need to add any file associations yourself. Importantly, neocaml uses
two separate modes: neocaml-mode for .ml files (using the ocaml
tree-sitter grammar) and neocaml-interface-mode for .mli files (using the
ocaml-interface grammar). Both must be mapped correctly for font-lock and
indentation to work.
Warning
If you previously used caml-mode or tuareg, remove any manual
auto-mode-alist entries for OCaml files from your config. A common
caml-mode pattern like:
(add-to-list 'auto-mode-alist '("\\.ml[iylp]?$" . caml-mode))
maps .mli files to a single mode. Replacing caml-mode with
neocaml-mode in such an entry will break .mli files because they
need neocaml-interface-mode, not neocaml-mode. The simplest fix is
to delete the entry entirely and let neocaml's autoloads handle it.
If you need to keep tuareg or caml-mode installed alongside neocaml,
make sure neocaml loads last so its auto-mode-alist entries win:
(use-package neocaml
:ensure t
:after tuareg) ; loads after tuareg, so neocaml's auto-mode-alist entries win
Unsupported file types¶
caml-mode and tuareg also handle .mll (ocamllex), .mly (ocamlyacc/menhir),
and .mlp (camlp4/camlp5) files. neocaml now supports .mll files via
neocaml-ocamllex-mode (with full OCaml syntax highlighting inside { }
blocks via language injection), and .mly files via neocaml-menhir-mode
(also with OCaml injection). The language injection requires Emacs 30+; on
Emacs 29 the modes still work but embedded OCaml code is not fontified.
.mlp (camlp4/camlp5) files are not supported.
Using the legacy dune-mode¶
neocaml provides its own neocaml-dune-mode for editing dune files. If you
prefer to keep using the dune-mode from the
dune package, override the auto-mode-alist
entries after neocaml loads:
(with-eval-after-load 'neocaml-dune
(add-to-list 'auto-mode-alist '("/dune\\'" . dune-mode))
(add-to-list 'auto-mode-alist '("/dune-project\\'" . dune-mode))
(add-to-list 'auto-mode-alist '("/dune-workspace\\'" . dune-mode)))
The neocaml-dune-interaction-mode (build/test/clean commands) works with
either major mode -- it doesn't depend on neocaml-dune-mode.
Keybinding differences¶
Most REPL keybindings (C-c C-z, C-c C-r, C-c C-b, C-c C-l)
work the same way across all three modes. The table below lists the
bindings that actually differ.
| Keybinding | neocaml | tuareg / caml-mode |
|---|---|---|
C-c C-c |
compile (or neocaml-repl-send-definition with REPL minor mode) |
Eval phrase (caml-mode) |
C-c C-a |
Toggle .ml/.mli | Not bound |
C-c C-p |
Send phrase to REPL | Not bound |
C-c C-i |
Interrupt REPL | Not bound |
C-c C-k |
Clear REPL buffer | Not bound |
Note
neocaml's REPL keybindings require enabling
neocaml-repl-minor-mode. See REPL Integration.
Merlin vs Eglot¶
Merlin works fine with neocaml -- it's a minor mode that hooks into any major
mode. That said, you might want to consider switching to Eglot + ocamllsp,
which provides the same features (completion, type display, jump-to-definition)
via the standard LSP protocol. Eglot is built into Emacs 29+ and neocaml
includes the necessary configuration out of the box. See the
ocaml-eglot section for OCaml-specific extensions like type
enclosing and case analysis.
Keeping tuareg's indentation¶
If you prefer tuareg's SMIE-based indentation over neocaml's tree-sitter indentation, you can use it directly -- see the Indentation section for the setup snippet.
What you gain¶
- Tree-sitter powered font-locking (more accurate, 4 configurable levels)
- Tree-sitter powered navigation (sexp, sentence, defun)
- Built-in Eglot integration with automatic language ID configuration
_builddirectory awareness- Simpler, more maintainable codebase
What you lose¶
ocamldebugintegration (neocaml does not include a debugger frontend, but you can use dape with ocamlearlybird instead - see Debugging).mlp(camlp4/camlp5) file support- Electric comment delimiters (
(inserting(* *)inside comments) - Code templates / skeletons