Improve ocaml language support
See README.org. Signed-off-by: Edwin Török <edwin@etorok.net>
This commit is contained in:
parent
476782e6d4
commit
064579e33e
4 changed files with 198 additions and 6 deletions
79
modules/lang/ocaml/README.org
Normal file
79
modules/lang/ocaml/README.org
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
#+TITLE: :lang ocaml
|
||||||
|
|
||||||
|
This module adds [[https://ocaml.org/][OCaml]] support, powered by [[https://github.com/ocaml/tuareg][tuareg-mode]].
|
||||||
|
|
||||||
|
+ Code completion, look up documentation, and code navigation ([[https://github.com/ocaml/merlin/wiki/emacs-from-scratch][merlin]])
|
||||||
|
+ REPL ([[https://github.com/ocaml-community/utop][utop]])
|
||||||
|
+ Syntax-checking (~merlin~ with [[https://github.com/flycheck/flycheck-ocaml][flycheck-ocaml]])
|
||||||
|
+ Auto-indentation ([[https://github.com/OCamlPro/ocp-indent][ocp-indent]])
|
||||||
|
+ Code formatting ([[https://github.com/ocaml-ppx/ocamlformat][ocamlformat]])
|
||||||
|
+ Dune file format ([[http://dune.build/][dune]])
|
||||||
|
|
||||||
|
* Table of Contents :TOC:
|
||||||
|
- [[Module Flags][Module Flags]]
|
||||||
|
- [[Prerequisites][Prerequisites]]
|
||||||
|
- [[Features][Features]]
|
||||||
|
- [[Configuration][Configuration]]
|
||||||
|
- [[Appendix][Appendix]]
|
||||||
|
- [[Commands][Commands]]
|
||||||
|
- [[Hacks][Hacks]]
|
||||||
|
|
||||||
|
* Module Flags
|
||||||
|
This module provides the ~+opam-site-lisp~ flag to compile editor support ~.el~
|
||||||
|
files from opam's =site-lisp= directory.
|
||||||
|
By default all editor plugins are installed from MELPA/GitHub even if the
|
||||||
|
corresponding =opam= package isn't installed.
|
||||||
|
If this flag is enabled then you must have all of the packages listed in
|
||||||
|
=package.el= installed via =opam=.
|
||||||
|
|
||||||
|
To enable this, use:
|
||||||
|
#+BEGIN_SRC emacs-lisp
|
||||||
|
(doom! :lang (ocaml +opam-site-lisp))
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
* Prerequisites
|
||||||
|
It is highly recommended to install [[http://opam.ocaml.org/][opam]].
|
||||||
|
To get all the features you should also install these ~opam~ packages, however
|
||||||
|
they are not all required (features should be disabled gracefully when a tool is
|
||||||
|
missing):
|
||||||
|
#+BEGIN_SRC shell
|
||||||
|
opam install merlin utop ocp-indent dune ocamlformat
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
* Features
|
||||||
|
+ the following files should have syntax highlighting support:
|
||||||
|
~.ml{i,p,y,}~, ~.eliom{i,}~, ~jbuild~, ~dune~, ~opam~
|
||||||
|
+ =merlin-mode= is activated whenever a =.merlin= file is found (including in a
|
||||||
|
parent directory) and =ocamlmerlin= executable is present
|
||||||
|
+ line-based auto-indentation is provided by =ocp-indent= when installed
|
||||||
|
|
||||||
|
* Configuration
|
||||||
|
+ if =:completion company= is enabled then autocomplete is provided by =merlin=
|
||||||
|
+ when =:feature syntax-checker= is enabled then =flycheck-ocaml= is activated
|
||||||
|
to do on-the-fly syntax/type checking via =merlin=, otherwise this is only
|
||||||
|
done when the file is saved.
|
||||||
|
+ spell checking is activated in comments if =:feature spellcheck= is actived
|
||||||
|
+ a REPL is provided if =utop= is installed and =:feature eval= is actived
|
||||||
|
+ if =:editor format= is enabled, the =ocamlformat= executable is available and
|
||||||
|
there is an =.ocamlformat= file present then =format-all-buffer= is bound to
|
||||||
|
=ocamlformat=, otherwise to =ocp-indent=
|
||||||
|
|
||||||
|
Run =make install= to install all packages, and =make doctor= to diagnose missing tools.
|
||||||
|
|
||||||
|
* Appendix
|
||||||
|
** Commands
|
||||||
|
| command | key / ex command | description |
|
||||||
|
|------------------------------+------------------+------------------------------------|
|
||||||
|
| =merlin-type-enclosing= | =SPC m t= | display type under point |
|
||||||
|
| =tuareg-find-alternate-file= | =SPC m a= | switch between =.ml= and =.mli= |
|
||||||
|
| =merlin-locate= | =gd= | lookup definition |
|
||||||
|
| =merlin-occurences= | =SPC c D= | lookup references |
|
||||||
|
| =merlin-document= | =K= | lookup documentation |
|
||||||
|
| =utop= | =SPC o r= | open =utop= as REPL |
|
||||||
|
| =utop-eval-region= | =SPC c e= | evaluate selected region in =utop= |
|
||||||
|
|
||||||
|
** Hacks
|
||||||
|
+ =set-pretty-symbols!= is called with the full tuareg prettify symbol list, this
|
||||||
|
can cause columns to change as certain keywords are shortened (e.g. =fun=
|
||||||
|
becomes \lambda.
|
||||||
|
+ =tuareg-opam-update-env= is called the first time =tuareg= is loaded
|
|
@ -1,13 +1,83 @@
|
||||||
;;; lang/ocaml/config.el -*- lexical-binding: t; -*-
|
;;; lang/ocaml/config.el -*- lexical-binding: t; -*-
|
||||||
|
|
||||||
(def-package! tuareg
|
;; def-project-mode!/associate! doesn't work when a
|
||||||
:mode ("\\.ml[4ilpy]?\\'" . tuareg-mode))
|
;; package is lazy loaded, and everything is compiled
|
||||||
|
|
||||||
|
(def-package! tuareg
|
||||||
|
:defer t ;; modes set by autoload
|
||||||
|
:config
|
||||||
|
;; tuareg-mode has the prettify symbols itself
|
||||||
|
(set-pretty-symbols! 'tuareg-mode :alist
|
||||||
|
(append tuareg-prettify-symbols-basic-alist
|
||||||
|
tuareg-prettify-symbols-extra-alist))
|
||||||
|
(setq tuareg-prettify-symbols-full t)
|
||||||
|
;; Use opam to set environment
|
||||||
|
(setq tuareg-opam-insinuate t)
|
||||||
|
(tuareg-opam-update-env (tuareg-opam-current-compiler))
|
||||||
|
;; Spell-check comments
|
||||||
|
(when (featurep! :feature spellcheck)
|
||||||
|
(add-hook 'tuareg-mode-hook #'flyspell-prog-mode)))
|
||||||
|
|
||||||
(def-package! merlin
|
(def-package! merlin
|
||||||
:after tuareg
|
:after tuareg
|
||||||
:hook (tuareg-mode . merlin-mode)
|
:init
|
||||||
|
(set-lookup-handlers! 'tuareg-mode
|
||||||
|
:definition #'merlin-locate
|
||||||
|
:references #'merlin-occurrences
|
||||||
|
:documentation #'merlin-document)
|
||||||
|
(defun +ocaml|init-merlin ()
|
||||||
|
(when (and (projectile-locate-dominating-file default-directory ".merlin")
|
||||||
|
(executable-find "ocamlmerlin"))
|
||||||
|
(merlin-mode)))
|
||||||
|
(add-hook 'tuareg-mode-hook #'+ocaml|init-merlin)
|
||||||
|
|
||||||
:config
|
:config
|
||||||
|
(map! :map tuareg-mode-map
|
||||||
|
:localleader
|
||||||
|
:n "t" #'merlin-type-enclosing
|
||||||
|
:n "a" #'tuareg-find-alternate-file)
|
||||||
(set-company-backend! 'tuareg-mode 'merlin-company-backend)
|
(set-company-backend! 'tuareg-mode 'merlin-company-backend)
|
||||||
(after! company
|
(setq merlin-completion-with-doc t))
|
||||||
(remove-hook 'company-backends 'merlin-company-backend)))
|
|
||||||
|
(def-package! flycheck-ocaml
|
||||||
|
:when (featurep! :feature syntax-checker)
|
||||||
|
:after merlin
|
||||||
|
:config
|
||||||
|
;; Disable Merlin's own error checking
|
||||||
|
(setq merlin-error-after-save nil)
|
||||||
|
;; Enable Flycheck checker
|
||||||
|
(flycheck-ocaml-setup))
|
||||||
|
|
||||||
|
(def-package! ocp-indent
|
||||||
|
;; must be careful to always defer this, it has autoloads that adds hooks
|
||||||
|
;; which we do not want if the executable can't be found
|
||||||
|
:defer t
|
||||||
|
:init
|
||||||
|
(defun +ocaml|init-ocp-indent ()
|
||||||
|
(when (executable-find "ocp-indent")
|
||||||
|
(ocp-setup-indent)))
|
||||||
|
(add-hook 'tuareg-mode-hook #'+ocaml|init-ocp-indent))
|
||||||
|
|
||||||
|
(def-package! utop
|
||||||
|
:defer t ;; loaded by hook below
|
||||||
|
:when (featurep! :feature eval)
|
||||||
|
:init
|
||||||
|
(set-repl-handler! 'tuareg-mode #'utop)
|
||||||
|
(set-eval-handler! 'tuareg-mode #'utop-eval-region)
|
||||||
|
(defun +ocaml|init-utop ()
|
||||||
|
(when (executable-find "utop")
|
||||||
|
(utop-minor-mode)))
|
||||||
|
(add-hook 'tuareg-mode-hook #'+ocaml|init-utop))
|
||||||
|
|
||||||
|
(def-package! ocamlformat
|
||||||
|
:after tuareg
|
||||||
|
:commands (ocamlformat)
|
||||||
|
:init
|
||||||
|
(set-formatter! 'ocamlformat #'ocamlformat
|
||||||
|
:modes '(caml-mode tuareg-mode))
|
||||||
|
(defun +ocaml|init-ocamlformat ()
|
||||||
|
(setq +format-with 'ocp-indent)
|
||||||
|
(when (and (executable-find "ocamlformat")
|
||||||
|
(locate-dominating-file default-directory ".ocamlformat"))
|
||||||
|
(setq +format-with 'ocamlformat)))
|
||||||
|
(add-hook 'tuareg-mode-hook #'+ocaml|init-ocamlformat))
|
||||||
|
|
18
modules/lang/ocaml/doctor.el
Normal file
18
modules/lang/ocaml/doctor.el
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
;; -*- lexical-binding: t; no-byte-compile: t; -*-
|
||||||
|
;;; lang/ocaml/doctor.el
|
||||||
|
|
||||||
|
(unless (executable-find "ocamlmerlin")
|
||||||
|
(warn! "Couldn't find ocamlmerlin. Lookup, completion and syntax checking won't work."))
|
||||||
|
|
||||||
|
;; Tuareg can still indent
|
||||||
|
(unless (executable-find "ocp-indent")
|
||||||
|
(warn! "Couldn't find ocp-indent. Auto-indentation will be less precise."))
|
||||||
|
|
||||||
|
(when (featurep! :feature eval)
|
||||||
|
(unless (executable-find "utop")
|
||||||
|
(warn! "Couldn't find utop. REPL won't be available")))
|
||||||
|
|
||||||
|
(unless (executable-find "dune")
|
||||||
|
(warn! "Couldn't find dune. Won't be able to highlight dune files"))
|
||||||
|
|
||||||
|
;; ocamlformat is optional, don't warn about it
|
|
@ -1,5 +1,30 @@
|
||||||
;; -*- no-byte-compile: t; -*-
|
;; -*- no-byte-compile: t; -*-
|
||||||
;;; lang/ocaml/packages.el
|
;;; lang/ocaml/packages.el
|
||||||
|
|
||||||
|
(when (featurep! +opam-site-lisp)
|
||||||
|
(defvar +ocaml-elisp-dir
|
||||||
|
(when (executable-find "opam")
|
||||||
|
(let ((opam-share (ignore-errors (car (process-lines "opam" "config" "var" "share" "--safe")))))
|
||||||
|
(when (and opam-share (file-directory-p opam-share))
|
||||||
|
(expand-file-name "emacs/site-lisp" opam-share)))))
|
||||||
|
|
||||||
|
(defmacro localpackage! (name)
|
||||||
|
`(package! ,name :recipe (:fetcher file :path ,+ocaml-elisp-dir)))
|
||||||
|
|
||||||
|
(localpackage! opam-site-lisp))
|
||||||
|
|
||||||
(package! tuareg)
|
(package! tuareg)
|
||||||
|
|
||||||
|
(when (featurep! :feature syntax-checker)
|
||||||
|
(package! flycheck-ocaml))
|
||||||
|
|
||||||
|
(unless (featurep! +opam-site-lisp)
|
||||||
(package! merlin)
|
(package! merlin)
|
||||||
|
(package! ocp-indent)
|
||||||
|
(when (featurep! :feature eval)
|
||||||
|
(package! utop))
|
||||||
|
(when (featurep! :editor format)
|
||||||
|
;; by default quelpa generated a version 0pre0.20180929.192844, which got parsed into (0 -1 0 ...), which when compared with version nil (0)
|
||||||
|
;; in package-installed-p always yielded false
|
||||||
|
(package! ocamlformat :recipe (:fetcher github :repo "ocaml-ppx/ocamlformat" :files ("emacs/*.el"))))
|
||||||
|
(package! dune :recipe (:fetcher github :repo "ocaml/dune" :files ("editor-integration/emacs/*.el"))))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue