Skip to content

Commit

Permalink
ocaml: refactor layer to make it better for spacemacs idioms
Browse files Browse the repository at this point in the history
Fix activation of auto-complete
Sort packages
Improvement with hook usage
Move opam init function in funcs.el and call it only in tuareg config
Fix errors if opam is not installed, warn if it has not been found
Small edition of the README
Rename additional REPL functions with spacemacs prefix
  • Loading branch information
syl20bnr committed May 12, 2015
1 parent 626b4d6 commit db1821a
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 139 deletions.
23 changes: 14 additions & 9 deletions contrib/lang/ocaml/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
- [Ocaml contribution layer for Spacemacs](#ocaml-contribution-layer-for-spacemacs)
- [Description](#description)
- [Install](#install)
- [Layer](#layer)
- [OPAM packages](#opam-packages)
- [Key Bindings](#key-bindings)
- [REPL (utop)](#repl-utop)
Expand All @@ -19,26 +20,26 @@

This is a very basic layer for editing ocaml files.

- Syntax highlighting via `tuareg-mode`
- Error reporting, completion and type display via `merlin`
- Syntax highlighting (major-mode) via [tuareg-mode][]
- Error reporting, completion and type display via [merlin][]
- auto-completion with company mode via [merlin][]
- syntax-checking via [flycheck-ocaml][]

## Install

### Layer

To use this contribution add it to your `~/.spacemacs`

```elisp
(setq-default dotspacemacs-configuration-layers '(ocaml))
```

Optional configuration layers supported:
* auto-completion (company mode via merlin)
* syntax-checking (flycheck-ocaml)

### OPAM packages

This layer requires some [opam](http://opam.ocaml.org) packages:

- `merlin`
- `merlin` for auto-completion
- `utop`
- `ocp-indent`

Expand Down Expand Up @@ -66,11 +67,15 @@ opam install merlin utop ocp-indent
<kbd>SPC m s P</kbd> | Send phrase to the REPL and switch to the REPL in `insert state`
<kbd>SPC m s r</kbd> | Send region to the REPL
<kbd>SPC m s R</kbd> | Send region to the REPL and switch to the REPL in `insert state`
<kbd>C-j</kbd> | next item in history
<kbd>C-k</kbd> | previous item in history
<kbd>C-j</kbd> | (in REPL) next item in history
<kbd>C-k</kbd> | (in REPL) previous item in history

## TODO

- Add more proper spacemacs key-bindings for basic merlin tasks
- Add proper keybindings for ocamldebug
- Add more keybindings for tuareg-mode

[tuareg-mode]: https://github.com/ocaml/tuareg
[merlin]: https://github.com/the-lambda-church/merlin
[flycheck-ocaml]: https://github.com/diml/utop
31 changes: 0 additions & 31 deletions contrib/lang/ocaml/extensions.el

This file was deleted.

23 changes: 23 additions & 0 deletions contrib/lang/ocaml/funcs.el
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
;;; funcs.el --- ocaml Layer functions File for Spacemacs
;;
;; Copyright (c) 2012-2014 Sylvain Benner
;; Copyright (c) 2014-2015 Sylvain Benner & Contributors
;;
;; Author: Sylvain Benner <sylvain.benner@gmail.com>
;; URL: https://github.com/syl20bnr/spacemacs
;;
;; This file is not part of GNU Emacs.
;;
;;; License: GPLv3

(defun spacemacs//init-ocaml-opam ()
(if (executable-find "opam")
(let ((share (substring (shell-command-to-string
"opam config var share 2> /dev/null") 0 -1)))
(when share
(setq opam-share share
opam-load-path (concat share "/emacs/site-lisp")))
(add-to-list 'load-path opam-load-path))
(spacemacs-buffer/warning
(concat "Cannot find \"opam\" executable. "
"The ocaml layer won't work properly."))))
190 changes: 91 additions & 99 deletions contrib/lang/ocaml/packages.el
Original file line number Diff line number Diff line change
Expand Up @@ -12,120 +12,112 @@

(setq ocaml-packages
'(
tuareg
merlin
utop
ocp-indent
company
flycheck
flycheck-ocaml
;; package ocamls go here
merlin
ocp-indent
tuareg
utop
))

(defun ocaml/init-tuareg ()
(add-hook 'tuareg-mode-hook #'merlin-mode)
(evil-leader/set-key-for-mode 'tuareg-mode
"mcc" 'compile
)
;; don't auto-close apostrophes (type 'a = foo)
(when (fboundp 'sp-local-pair)
(sp-local-pair 'tuareg-mode "'" nil :actions nil)
)
)
(defun ocaml/post-init-company ()
(spacemacs|add-company-hook merlin-mode))

(defun ocaml/opam ()
(setq opam-share (substring (shell-command-to-string "opam config var share 2> /dev/null") 0 -1))
(setq opam-load-path (concat opam-share "/emacs/site-lisp"))
(add-to-list 'load-path opam-load-path))
(when (configuration-layer/layer-usedp 'syntax-checking)
(defun ocaml/init-flycheck-ocaml ()
(use-package flycheck-ocaml
:if (configuration-layer/package-usedp 'flycheck)
:defer t
:init
(progn
(add-to-hook 'merlin-mode-hook '(flycheck-mode
flycheck-ocaml-setup))
(eval-after-load 'merlin
(setq merlin-use-auto-complete-mode nil))))))

(defun ocaml/init-utop ()
(use-package utop
(defun ocaml/init-merlin ()
(use-package merlin
:defer t
:init
(autoload 'utop "utop" "Toplevel for OCaml" t)
(autoload 'utop-minor-mode "utop" "Minor mode for utop" t)
(add-hook 'tuareg-mode-hook 'utop-minor-mode)
:config
;; Setup environment variables using opam
(dolist (var (car (read-from-string (shell-command-to-string "opam config env --sexp"))))
(setenv (car var) (cadr var)))
;; Update the emacs path
(setq exec-path (append (parse-colon-path (getenv "PATH"))
(list exec-directory)))
(defun utop-eval-phrase-and-go ()
(interactive)
(utop-eval-phrase)
(utop))
(defun utop-eval-buffer-and-go ()
(interactive)
(utop-eval-buffer)
(utop))
(defun utop-eval-region-and-go (start end)
(interactive "r")
(utop-eval-region start end)
(utop))
(evil-leader/set-key-for-mode 'tuareg-mode
"msb" 'utop-eval-buffer
"msB" 'utop-eval-buffer-and-go
"msi" 'utop
"msp" 'utop-eval-phrase
"msP" 'utop-eval-phrase-and-go
"msr" 'utop-eval-region
"msR" 'utop-eval-region-and-go
)
)
(define-key utop-mode-map (kbd "C-j") 'utop-history-goto-next)
(define-key utop-mode-map (kbd "C-k") 'utop-history-goto-prev)
)
(progn
(add-hook 'tuareg-mode-hook 'merlin-mode)
;; disable integration with auto-complete, we use flycheck
(set-default 'merlin-use-auto-complete-mode nil)
(push 'merlin-company-backend company-backends-merlin-mode)
(evil-leader/set-key-for-mode 'tuareg-mode
"met" 'merlin-type-enclosing
"mgg" 'merlin-locate
;;"mhh" 'merlin-document
))))

(defun ocaml/init-ocp-indent ()
(use-package ocp-indent
:defer t
:init
(ocaml/opam)
)
)
(add-hook 'tuareg-mode-hook 'ocp-indent-caml-mode-setup)))

(defun ocaml/init-merlin ()
(use-package merlin
(defun ocaml/init-tuareg ()
(use-package tuareg
:defer t
:init
(ocaml/opam)
(set-default 'merlin-use-auto-complete-mode 'easy)
(when (configuration-layer/package-usedp 'company)
(push 'merlin-company-backend company-backends-merlin-mode))
)
(evil-leader/set-key-for-mode 'tuareg-mode
"mgg" 'merlin-locate
"met" 'merlin-type-enclosing
;; "mhh" 'merlin-document
)
)
(progn
(spacemacs//init-ocaml-opam)
(evil-leader/set-key-for-mode 'tuareg-mode
"mcc" 'compile))
:config
(when (fboundp 'sp-local-pair)
;; don't auto-close apostrophes (type 'a = foo)
(sp-local-pair 'tuareg-mode "'" nil :actions nil))))

(when (configuration-layer/layer-usedp 'auto-completion)
;; Hook company to merlin-mode
(defun ocaml/post-init-company ()
(spacemacs|add-company-hook merlin-mode)
))
(defun ocaml/init-utop ()
(use-package utop
:defer t
:init (add-hook 'tuareg-mode-hook 'utop-minor-mode)
:config
(progn
;; Setup environment variables using opam
(if (executable-find "opam")
(let ((vars (car (read-from-string
(shell-command-to-string "opam config env --sexp")))))
(dolist (var vars)
(setenv (car var) (cadr var))))
(spacemacs-buffer/warning "Cannot find \"opam\" executable."))
;; Update the emacs path
(setq exec-path (append (parse-colon-path (getenv "PATH"))
(list exec-directory)))

(when (configuration-layer/layer-usedp 'syntax-checking)
(defun ocaml/init-flycheck-ocaml ()
(use-package flycheck-ocaml
:if (configuration-layer/package-usedp 'flycheck)
:defer t
:init
(add-hook 'merlin-mode-hook 'flycheck-mode)
(with-eval-after-load 'merlin
;; Disable Merlin's own error checking
(setq merlin-error-after-save nil)
;; Enable Flycheck checker
(flycheck-ocaml-setup))
)))
(defun spacemacs/utop-eval-phrase-and-go ()
"Send phrase to REPL and evaluate it and switch to the REPL in
`insert state'"
(interactive)
(utop-eval-phrase)
(utop)
(evil-insert-state))

;; For each package, define a function ocaml/init-<package-ocaml>
;;
;; (defun ocaml/init-my-package ()
;; "Initialize my package"
;; )
;;
;; Often the body of an initialize function uses `use-package'
;; For more info on `use-package', see readme:
;; https://github.com/jwiegley/use-package
(defun spacemas/utop-eval-buffer-and-go ()

This comment has been minimized.

Copy link
@person808

person808 May 12, 2015

Contributor

@syl20bnr You made a typo here :)

This comment has been minimized.

Copy link
@syl20bnr

syl20bnr May 12, 2015

Author Owner

@person808 oops, thank you :-) you can PR a fix if you want, I'm going to bed.

"Send buffer to REPL and evaluate it and switch to the REPL in
`insert state'"
(interactive)
(utop-eval-buffer)
(utop)
(evil-insert-state))

(defun spacemacs/utop-eval-region-and-go (start end)

This comment has been minimized.

Copy link
@person808

person808 May 12, 2015

Contributor

@syl20bnr same here

"Send region to REPL and evaluate it and switch to the REPL in
`insert state'"
(interactive "r")
(utop-eval-region start end)
(utop)
(evil-insert-state))

(evil-leader/set-key-for-mode 'tuareg-mode
"msb" 'utop-eval-buffer
"msB" 'spacemas/utop-eval-buffer-and-go
"msi" 'utop
"msp" 'utop-eval-phrase
"msP" 'spacemacs/utop-eval-phrase-and-go
"msr" 'utop-eval-region
"msR" 'spacemacs/utop-eval-region-and-go))
(define-key utop-mode-map (kbd "C-j") 'utop-history-goto-next)
(define-key utop-mode-map (kbd "C-k") 'utop-history-goto-prev)))

0 comments on commit db1821a

Please sign in to comment.