Skip to content

Getting Started

Prerequisites

neocaml provides the editing experience, but you'll want OCaml tooling installed for building and LSP support:

  • OCaml (via opam, the OCaml package manager)
  • ocaml-lsp-server for Eglot integration: opam install ocaml-lsp-server
  • dune for building projects: opam install dune

Tip

If you launch Emacs from a desktop shortcut (e.g. Emacs.app on macOS) rather than a terminal, your shell's PATH may not be inherited, so ocamllsp and other tools won't be found. See Troubleshooting for the fix.

What works out of the box

Once neocaml is installed, the following works automatically with no configuration:

  • Font-lock (syntax highlighting) for .ml and .mli files, with 4 configurable levels
  • Indentation via tree-sitter
  • Navigation - beginning-of-defun, end-of-defun, forward-sexp, and more (see Code Navigation)
  • Imenu - jump to definitions with M-x imenu
  • File toggle - switch between .ml and .mli with C-c C-a
  • Comment commands - M-;, M-j, M-q all work with OCaml's (* ... *) comments
  • Clickable comments - URLs in comments are clickable, and issue references become clickable once bug-reference-url-format is set (see Configuration)
  • Eglot registration - M-x eglot starts ocamllsp automatically
  • Compilation - C-c C-c runs compile with OCaml error navigation
  • Additional file types - opam, dune, OCamllex, Menhir, and cram files get their own modes automatically
  • dune & opam completion - completion-at-point (C-M-i) offers stanza/field names and library/package names in dune and opam files (see opam, dune & Cram)

Features that need opt-in

Some features require a line or two of configuration to enable:

Feature How to enable
REPL integration (add-hook 'neocaml-base-mode-hook #'neocaml-repl-minor-mode) - see REPL
dune build commands (add-hook 'neocaml-base-mode-hook #'neocaml-dune-interaction-mode) - see dune
Prettify symbols (add-hook 'neocaml-base-mode-hook #'prettify-symbols-mode) - see Configuration
Code folding (add-hook 'neocaml-base-mode-hook #'outline-minor-mode) (Emacs 30+)
opam lint (add-hook 'neocaml-opam-mode-hook #'flymake-mode) - see opam
Format on save (setq neocaml-format-on-save t) - see Configuration

All of these use Emacs hooks - a way to run code when a mode is activated. You add them to your Emacs configuration (typically init.el).

Note

The examples use neocaml-base-mode-hook rather than neocaml-mode-hook. That's because neocaml-base-mode is the shared parent of both neocaml-mode (.ml) and neocaml-interface-mode (.mli), so hooks on it apply to both file types at once. Use neocaml-mode-hook or neocaml-interface-mode-hook if you want something only for one.

Typical Workflow

A typical session looks something like this: open an OCaml file and you get syntax highlighting and indentation right away. Start Eglot with M-x eglot for completion, type information, and jump-to-definition. Navigate between definitions with C-M-a / C-M-e, jump to a specific one with M-x imenu, and switch between .ml and .mli with C-c C-a. Run C-c C-c to compile and M-g n to jump to the first error.

For interactive development, enable the REPL minor mode and press C-c C-z to start a toplevel. Evaluate the current definition with C-c C-c or a region with C-c C-r, and switch back to your source with C-c C-z again.

Eglot (LSP)

neocaml auto-registers both modes with Eglot, so M-x eglot will start ocamllsp with the correct language IDs automatically. To start Eglot whenever you open an OCaml file:

(add-hook 'neocaml-base-mode-hook #'eglot-ensure)

Note

neocaml sets the eglot-language-id symbol property on both modes ("ocaml" for .ml and "ocaml.interface" for .mli), so the correct language IDs are sent to the server automatically.

ocaml-eglot

ocaml-eglot is a lightweight package that enhances Eglot for OCaml by exposing custom LSP requests from ocamllsp - type enclosing, case analysis, hole navigation, and more. It works with neocaml out of the box:

(use-package ocaml-eglot
  :ensure t
  :hook
  (neocaml-base-mode . ocaml-eglot)
  (ocaml-eglot . eglot-ensure))

Compilation

C-c C-c runs M-x compile, and neocaml registers an OCaml-specific error regexp so that next-error (M-g n) and previous-error (M-g p) jump directly to the source locations reported by the OCaml compiler, including errors, warnings, alerts, and exception backtraces.

For a richer build workflow with dune (build, test, clean, fmt, watch mode), see dune commands.

In a dune project, compile-command defaults to dune build, so C-c C-c and M-x project-compile build the project out of the box.

Project Integration

neocaml teaches the built-in project.el that a directory containing a dune-project file is a project root. That makes the project-wide commands work for OCaml projects - project-find-file (C-x p f), project-find-regexp (C-x p g), project-compile (C-x p c), and the rest - rooted at the dune project, with _build/ and _opam/ ignored.

Version-control detection still takes precedence, so a project under git roots at the repository as usual; the dune-project detection mainly helps projects that aren't under version control.

neocaml also registers sensible modes for a few other OCaml-related files:

  • .ocamlinit opens in neocaml-mode (it's OCaml toplevel startup code)
  • .ocamlformat and .ocp-indent open in conf-unix-mode (key = value config files with # comments)
  • .eliom / .eliomi (Ocsigen) are handled by ff-find-other-file for toggling between implementation and interface

Useful Commands

Command Description
M-x neocaml-version Show the installed neocaml version
M-x neocaml-bug-report-info Copy debug info (versions, grammar status) to the kill ring
M-x neocaml-report-bug Open the neocaml issue tracker in your browser
M-x neocaml-browse-ocaml-docs Open the OCaml documentation in your browser
M-x neocaml-cycle-indent-function Toggle between tree-sitter and indent-relative indentation
C-c C-f (neocaml-format-buffer) Format the buffer with ocamlformat (see Configuration)

These are also available from the OCaml menu.

Companion Packages

These packages work well with neocaml:

What's Next?