lang/ocaml: major refactor

+ Load flyspell-mode a little later, to allow file/dir-local variables
  to customize flyspell.
+ Ensure setters (in merlin's config) are used as late as possible, by
  making merlin's (and all the other packages) config run after tuareg
  is loaded.
+ As discussed, installing packages locally is a bit unreliable, so I'm
  commenting out the +ocaml-site-lisp flag, and rely solely on packages
  from MELPA.
+ Add optional ocamlformat check in doctor.el and conditionally load the
  ocamlformat package (if editor/format is enabled).
+ Add docstrings to init hooks.
This commit is contained in:
Henrik Lissner 2018-10-16 02:45:12 -04:00
parent 4e81605463
commit d1d9cffcc8
3 changed files with 103 additions and 89 deletions

View file

@ -1,83 +1,91 @@
;;; lang/ocaml/config.el -*- lexical-binding: t; -*- ;;; lang/ocaml/config.el -*- lexical-binding: t; -*-
;; def-project-mode!/associate! doesn't work when a (after! tuareg
;; 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 ;; tuareg-mode has the prettify symbols itself
(set-pretty-symbols! 'tuareg-mode :alist (set-pretty-symbols! 'tuareg-mode :alist
(append tuareg-prettify-symbols-basic-alist (append tuareg-prettify-symbols-basic-alist
tuareg-prettify-symbols-extra-alist)) tuareg-prettify-symbols-extra-alist))
;; harmless if `prettify-symbols-mode' isn't active
(setq tuareg-prettify-symbols-full t) (setq tuareg-prettify-symbols-full t)
;; Use opam to set environment ;; Use opam to set environment
(setq tuareg-opam-insinuate t) (setq tuareg-opam-insinuate t)
(tuareg-opam-update-env (tuareg-opam-current-compiler)) (tuareg-opam-update-env (tuareg-opam-current-compiler))
;; Spell-check comments ;; Spell-check comments
(when (featurep! :feature spellcheck) (when (featurep! :feature spellcheck)
(add-hook 'tuareg-mode-hook #'flyspell-prog-mode))) (add-hook 'tuareg-mode-local-vars-hook #'flyspell-prog-mode))
(def-package! merlin
:after tuareg
: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 (def-package! merlin
(map! :map tuareg-mode-map :defer t
:localleader :init
:n "t" #'merlin-type-enclosing (defun +ocaml|init-merlin ()
:n "a" #'tuareg-find-alternate-file) "Activate `merlin-mode' if the ocamlmerlin executable exists and the
(set-company-backend! 'tuareg-mode 'merlin-company-backend) current project possesses a .merlin file."
(setq merlin-completion-with-doc t)) (when (and (projectile-locate-dominating-file default-directory ".merlin")
(executable-find "ocamlmerlin"))
(merlin-mode)))
(add-hook 'tuareg-mode-hook #'+ocaml|init-merlin)
(def-package! flycheck-ocaml (set-company-backend! 'tuareg-mode 'merlin-company-backend)
:when (featurep! :feature syntax-checker) (set-lookup-handlers! 'tuareg-mode
:after merlin :definition #'merlin-locate
:config :references #'merlin-occurrences
;; Disable Merlin's own error checking :documentation #'merlin-document)
(setq merlin-error-after-save nil) :config
;; Enable Flycheck checker (setq merlin-completion-with-doc t)
(flycheck-ocaml-setup))
(def-package! ocp-indent (map! :map tuareg-mode-map
;; must be careful to always defer this, it has autoloads that adds hooks :localleader
;; which we do not want if the executable can't be found :n "t" #'merlin-type-enclosing
:defer t :n "a" #'tuareg-find-alternate-file))
: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 (def-package! flycheck-ocaml
:after tuareg :when (featurep! :feature syntax-checker)
:commands (ocamlformat) :after merlin
:init :config
(set-formatter! 'ocamlformat #'ocamlformat ;; Disable Merlin's own error checking
:modes '(caml-mode tuareg-mode)) (setq merlin-error-after-save nil)
(defun +ocaml|init-ocamlformat () ;; Enable Flycheck checker
(setq +format-with 'ocp-indent) (flycheck-ocaml-setup))
(when (and (executable-find "ocamlformat")
(locate-dominating-file default-directory ".ocamlformat"))
(setq +format-with 'ocamlformat))) (def-package! utop
(add-hook 'tuareg-mode-hook #'+ocaml|init-ocamlformat)) :when (featurep! :feature eval)
:defer t ; loaded by hook below
: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! 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 ()
"Run `ocp-setup-indent', so long as the ocp-indent binary exists."
(when (executable-find "ocp-indent")
(ocp-setup-indent)))
(add-hook 'tuareg-mode-hook #'+ocaml|init-ocp-indent))
(def-package! ocamlformat
:when (featurep! :editor format)
:commands ocamlformat
:init
(set-formatter! 'ocamlformat #'ocamlformat
:modes '(caml-mode tuareg-mode))
;; TODO Fix region-based formatting support
(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)))

View file

@ -2,11 +2,11 @@
;;; lang/ocaml/doctor.el ;;; lang/ocaml/doctor.el
(unless (executable-find "ocamlmerlin") (unless (executable-find "ocamlmerlin")
(warn! "Couldn't find ocamlmerlin. Lookup, completion and syntax checking won't work.")) (warn! "Couldn't find ocamlmerlin. Lookup, completion and syntax checking won't work"))
;; Tuareg can still indent ;; Tuareg can still indent
(unless (executable-find "ocp-indent") (unless (executable-find "ocp-indent")
(warn! "Couldn't find ocp-indent. Auto-indentation will be less precise.")) (warn! "Couldn't find ocp-indent. Auto-indentation will be less precise"))
(when (featurep! :feature eval) (when (featurep! :feature eval)
(unless (executable-find "utop") (unless (executable-find "utop")
@ -15,4 +15,8 @@
(unless (executable-find "dune") (unless (executable-find "dune")
(warn! "Couldn't find dune. Won't be able to highlight dune files")) (warn! "Couldn't find dune. Won't be able to highlight dune files"))
(when (featurep! :editor format)
(unless (executable-find "ocamlformat")
(warn! "Couldn't find ocamlformat. Code-formatting will be unavailable")))
;; ocamlformat is optional, don't warn about it ;; ocamlformat is optional, don't warn about it

View file

@ -1,30 +1,32 @@
;; -*- 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)
(package! merlin)
(package! ocp-indent)
(when (featurep! :feature syntax-checker) (when (featurep! :feature syntax-checker)
(package! flycheck-ocaml)) (package! flycheck-ocaml))
(unless (featurep! +opam-site-lisp) (when (featurep! :feature eval)
(package! merlin) (package! utop))
(package! ocp-indent)
(when (featurep! :feature eval) (when (featurep! :editor format)
(package! utop)) ;; by default quelpa generated a version 0pre0.20180929.192844, which got
(when (featurep! :editor format) ;; parsed into (0 -1 0 ...), which when compared with version nil (0) in
;; by default quelpa generated a version 0pre0.20180929.192844, which got parsed into (0 -1 0 ...), which when compared with version nil (0) ;; package-installed-p always yielded false
;; in package-installed-p always yielded false (package! ocamlformat :recipe (:fetcher github :repo "ocaml-ppx/ocamlformat" :files ("emacs/*.el"))))
(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")))) (package! dune :recipe (:fetcher github :repo "ocaml/dune" :files ("editor-integration/emacs/*.el")))
;; (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)