merlin-eldoc icon indicating copy to clipboard operation
merlin-eldoc copied to clipboard

Type and doc on hover for OCaml and Reason in emacs

#+TITLE: Merlin backend for eldoc

[[https://melpa.org/#/merlin-eldoc][file:https://melpa.org/packages/merlin-eldoc-badge.svg]]

This package provides a merlin backend for eldoc. Its goal is to automatically (without using keybindings) provide information for the value under point in OCaml and ReasonML files. It also provides hints when calling a function. This information is obtained from merlin.

Please see the [[file:CHANGES.org][changelog]] for recent improvements and list of changes between versions.

  • Features
  • display type
  • display documentation
  • highlight occurrences
  • jump to other occurrences
  • provide the list of expected arguments (and their types) when calling a function
  • Installation

** use-package

Add this code to ~init.el~

#+BEGIN_SRC emacs-lisp (use-package merlin-eldoc :ensure t :hook ((reason-mode tuareg-mode caml-mode) . merlin-eldoc-setup)) #+END_SRC

** Quelpa

With quelpa already installed and configured:

#+BEGIN_SRC emacs-lisp (quelpa '(merlin-eldoc :fetcher github :repo "Khady/merlin-eldoc")) ;; Add a hook to start the mode automatically for OCaml and Reason (add-hook 'tuareg-mode-hook 'merlin-eldoc-setup) (add-hook 'reason-mode-hook 'merlin-eldoc-setup) #+END_SRC

** The more manual way

First the package has to be installed:

~M-x package-install RET merlin-eldoc RET~

Then this code should be added to ~init.el~

#+BEGIN_SRC emacs-lisp (require 'merlin-eldoc) (add-hook 'tuareg-mode-hook 'merlin-eldoc-setup) (add-hook 'reason-mode-hook 'merlin-eldoc-setup) #+END_SRC

  • Usage

If a hook has been configured, then there is nothing to do. The eldoc mode should be launched automatically when an OCaml file is visited. And merlin will provide type information when available.

Otherwise, it can be launched by executing ~M-x merlin-eldoc-setup~.

Once this is done, as soon as the point is on a word which is not a keyword, a type should be displayed in the echo area when the cursor doesn't move for some time.

It is possible to configure the shape of the results using both eldoc and merlin-eldoc configurations. It allows to choose on how many lines the result will fit, if the documentation can be truncated, how to concatenate type and documentation...

For eldoc, the value to configure is ~eldoc-echo-area-use-multiline-p~.

For merlin-eldoc, the easiest way it so use the customize interface to get access to all the possible values and the corresponding documentation.

#+BEGIN_SRC M-x merlin-eldoc-customize RET #+END_SRC

The main values are:

  • ~merlin-eldoc-doc~ to enable or disable the display of the documentation.
  • ~merlin-eldoc-type~ to enable or disable the display of the type.
  • ~merlin-eldoc-type-verbosity~ to control verbosity of the type. if ~'max~, it is equivalent to calling multiple times ~merlin-type-enclosing~.
  • ~merlin-eldoc-occurrences~ to enable or disable the highlighting of all the occurrences of the identifier at point.
  • ~merlin-eldoc-function-arguments~ to enable or disable the hints when applying arguments to a function.
  • ~merlin-eldoc-max-lines~ to set on how many lines should fit the message printed in the minibuffer.

If highlighting of occurrences is enabled, two functions are povided to jump to the previous or next occurrence of value under the point:

  • ~merlin-eldoc-jump-to-prev-occurrence~
  • ~merlin-eldoc-jump-to-next-occurrence~

They can be binded to keys for more convenient usage.

  • Configuration examples

** use-package

#+BEGIN_SRC emacs-lisp (use-package merlin-eldoc :after merlin :ensure t :custom (eldoc-echo-area-use-multiline-p t) ; use multiple lines when necessary (merlin-eldoc-max-lines 8) ; but not more than 8 (merlin-eldoc-type-verbosity 'min) ; don't display verbose types (merlin-eldoc-function-arguments nil) ; don't show function arguments (merlin-eldoc-doc nil) ; don't show the documentation :bind (:map merlin-mode-map ("C-c m p" . merlin-eldoc-jump-to-prev-occurrence) ("C-c m n" . merlin-eldoc-jump-to-next-occurrence)) :hook ((tuareg-mode reason-mode) . merlin-eldoc-setup)) #+END_SRC

** Quelpa

#+BEGIN_SRC emacs-lisp (quelpa '(merlin-eldoc :repo "Khady/merlin-eldoc" :fetcher github))

;; use multiple lines when necessary (setq eldoc-echo-area-use-multiline-p t)

;; but not more than 10 (setq merlin-eldoc-max-lines 10)

;; don't dedicate a line to the documentation (setq merlin-eldoc-max-lines-doc 'fit)

;; start merlin-eldoc when editing ocaml and reason files (add-hook 'tuareg-mode-hook #'merlin-eldoc-setup) (add-hook 'reason-mode-hook #'merlin-eldoc-setup) #+END_SRC

  • Examples

In the following examples, the cursor is at ~<-!->~. After the requisite idle time, eldoc will fire and show the corresponding information in the minibuffer.

  1. Type of a function

    #+BEGIN_SRC ocaml let my<-!->add ~f ~i = f +. (float i) #+END_SRC

    The information in the minibuffer will be:

    #+BEGIN_SRC ocaml f:float -> i:int -> float #+END_SRC

  2. Type and documentation of a function

    #+BEGIN_SRC ocaml (** [myadd f i] add f and i *) let my<-!->add ~f ~i = f +. (float i) #+END_SRC

    With type and doc enabled, it shows:

    #+BEGIN_SRC ocaml f:float -> i:int -> float (* [myadd f i] add f and i *) #+END_SRC

  3. Type and documentation of ~List.map~ limited to one line

    #+BEGIN_SRC ocaml List.map<-!-> #+END_SRC

    The exact result will depend on the width of the Emacs frame.

    #+BEGIN_SRC ocaml ('a -> 'b) -> 'a list -> 'b list (* [List.map f [a1; ...; an]] applies function [f] to [a1, ..., an], and builds... *) #+END_SRC

  4. Type and documentation of ~List.map~ on multiple lines

    #+BEGIN_SRC ocaml List.map<-!-> #+END_SRC

    If at least 4 lines are allowed:

    #+BEGIN_SRC ocaml (* [List.map f [a1; ...; an]] applies function [f] to [a1, ..., an], and builds the list [[f a1; ...; f an]] with the results returned by [f]. Not tail-recursive. *) ('a -> 'b) -> 'a list -> 'b list #+END_SRC

  5. Function application and argument types

    #+BEGIN_SRC ocaml let apply ~f ~i op = op @@ f +. (float i)

let v = apply <-!-> #+END_SRC

#+BEGIN_SRC ocaml (* expected type: ) float -> 'a ( labels: *) ~f:float -> ~i:int #+END_SRC

Depending on the value of ~merlin-eldoc-max-lines-function-arguments~ it can also be displayed on one line.

#+BEGIN_SRC ocaml (* expected type: ) float -> 'a ( labels: *) ~f:float -> ~i:int #+END_SRC

  1. Function application and argument types, with some arguments already given

    #+BEGIN_SRC ocaml let apply ~f ~i op = op @@ f +. (float i)

let v = apply ~i:3 <-!-> #+END_SRC

#+BEGIN_SRC ocaml (* expected type: ) float -> 'a ( labels: *) ~f:float #+END_SRC

  1. Type expected by a label

    #+BEGIN_SRC ocaml let apply ~f ~i op = op @@ f +. (float i)

let v = apply ~i:<-!-> #+END_SRC

#+BEGIN_SRC ocaml (* expected type: *) int #+END_SRC

Two videos are played bellow for a more visual demonstration.

  • Demo

Video showing the following features provided by this package:

  • Type of value at point
  • Highlight other occurrences of the identifier
  • Type hints while calling function
  • Documentation of function/value at point

All those operations are automatically called by ~eldoc~ when the cursor is idle for 0.5s.

#+ATTR_HTML: title="full demo video" [[https://d.khady.info/merlin-eldoc-long.ogv][file:full-demo.gif]]

Short video of demonstration with only type and documentation enabled:

#+ATTR_HTML: title="short demo video" [[https://d.khady.info/merlin-eldoc.ogv][file:demo.gif]]

Note that merlin is never called explicitly in this video. The only action is to move the pointer from one place to another.

  • License

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.