-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathontop-ocaml.el
284 lines (245 loc) · 9.7 KB
/
ontop-ocaml.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
;;; ontop-ocaml.el --- OCaml setup -*- lexical-binding: t; -*-
;; This file is part of Emacs ONTOP
;; https://github.com/monkeyjunglejuice/emacs.ontop
;;; Commentary:
;; You can also use this file/configuration independently from Emacs ONTOP
;; Load it from anywhere via `(load-file "/path/to/ontop-ocaml.el")'
;;
;; The aim of Emacs ONTOP is to provide a comprehensive OCaml setup.
;; Using this module, you will not need to load `opam-user-setup.el'
;; in Emacs, because the functionality is already included here.
;; It's nevertheless a good idea to install "user-setup" package
;; via "opam install user-setup" or `opam-install-dev-packages',
;; because some of its dependencies are quite useful.
;;; Code:
;; ____________________________________________________________________________
;;; USE-PACKAGE
;; <https://github.com/jwiegley/use-package>
(unless (package-installed-p 'use-package)
(package-refresh-contents)
(package-install 'use-package nil))
(eval-when-compile
(require 'use-package))
;; Alternatively, use the Emacs packages installed by Opam
;; (add-to-list 'load-path
;; (expand-file-name "~/.opam/default/share/emacs/site-lisp"))
;; ____________________________________________________________________________
;;; TUAREG
(use-package tuareg
:ensure t
:custom
(tuareg-electric-indent t)
(tuareg-display-buffer-on-eval t)
(tuareg-skip-after-eval-phrase nil)
;; (tuareg-interactive-program "ocaml -nopromptcont")
(tuareg-interactive-echo-phrase t)
(tuareg-interactive-read-only-input t)
(tuareg-interactive-scroll-to-bottom-on-output t)
:hook
(tuareg-mode . (lambda ()
(setq-local compile-command "dune build")))
(tuareg-interactive-mode . (lambda ()
(set-process-query-on-exit-flag
(get-process "OCaml") nil)))
:bind
;; Reach the REPL from anywhere via global key binding
(:map ctl-z-x-map
("o" . tuareg-run-ocaml))
(:map tuareg-mode-map
("C-c C-e" . tuareg-eval-phrase)
("C-c C-r" . tuareg-eval-region)
("C-c C-b" . tuareg-eval-buffer)))
;; ____________________________________________________________________________
;;; MERLIN
(use-package merlin
:ensure t
:custom
(merlin-completion-with-doc t)
(merlin-report-errors-in-lighter t)
(merlin-occurrences-show-buffer 'same)
:config
;; Don't show *merlin* buffer in buffers list
(when (boundp 'eon-boring-buffers)
(add-to-list 'eon-boring-buffers "\\`\\*merlin"))
:hook
((tuareg-mode tuareg-interactive-mode) . merlin-mode)
(tuareg-mode . merlin-use-merlin-imenu)
:bind
(:map merlin-mode-map
("C-M-p" . merlin-phrase-prev)
("C-M-n" . merlin-phrase-next)
("C-c C-t" . merlin-type-enclosing)
("C-c <up>" . merlin-type-enclosing-go-up)
("C-c <down>" . merlin-type-enclosing-go-down)
("C-c C-d" . merlin-document)
("C-c d" . merlin-destruct)
("C-c e e" . merlin-error-check)
("C-c e r" . merlin-error-reset)
("C-c e t" . merlin-toggle-view-errors)
("M-g p" . merlin-error-prev)
("M-g n" . merlin-error-next)))
;; ____________________________________________________________________________
;;; MERLIN-ELDOC
;; <https://github.com/Khady/merlin-eldoc>
(use-package merlin-eldoc
:ensure t
:custom
(merlin-eldoc-max-lines 3) ; but not more than 5
(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
:hook
(tuareg-mode . merlin-eldoc-setup)
:bind
(:map tuareg-mode-map
("C-M-b" . merlin-eldoc-jump-to-prev-occurrence)
("C-M-f" . merlin-eldoc-jump-to-next-occurrence)))
;; ____________________________________________________________________________
;;; REPL
;; Run the standard OCaml toplevel via `tuareg-run-ocaml' "C-c C-s".
;; It’s possible to integrate any toplevel with Dune projects:
;; 1. Navigate to your Dune project
;; 2. "M-x opam-update-env" and choose the Opam switch for your project
;; 3. Execute in the toplevel: #use_output "dune ocaml top";;
;; (or put this into a project-specific .ocamlinit file)
;; <https://dune.readthedocs.io/en/stable/toplevel-integration.html>
;; "C-c C-s": `tuareg-run-ocaml'
;; "C-c C-z": `tuareg-switch-to-repl'
;; "C-c C-e": `tuareg-eval-phrase'
;; "C-c C-r": `tuareg-eval-region'
;; "C-c C-b": `tuareg-eval-buffer'
;; . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
;;; ALTERNATIVE REPL
;; Alternatively, there's Utop that also intergrates with Dune projects.
;; <https://github.com/ocaml-community/utop>
;; <https://dune.readthedocs.io/en/stable/usage.html#launching-the-toplevel-repl>
;; Even though Utop works great in the shell, it works not so well in Emacs.
;; Right now there are some issues with autocompletion via `company-mode'.
;; If Utop dies at launch, try to fix it by updating the Opam environment
;; within your Dune project, either via "M-x opam-update-env" (recommended)
;; or "M-! eval $(opam env)".
;; The keybindings are the same as with the standard toplevel.
(use-package utop
:ensure t
:custom
;; Utop will start with Dune project awareness:
(utop-command "opam exec -- dune utop . -- -emacs")
:hook
(tuareg-mode . utop-minor-mode)
;; HACK Workaround to get completions in Utop from Merlin
(utop-mode . (lambda ()
(merlin-mode -1)
(merlin-mode +1)))
:bind
;; Reach `utop' from anywhere via global key binding
(:map ctl-z-x-map
("u" . utop)))
;; ____________________________________________________________________________
;;; INDENTATION
;; <https://github.com/OCamlPro/ocp-indent>
(use-package ocp-indent
:ensure t
:after merlin-mode
:hook
(tuareg-mode . ocp-setup-indent))
;; ____________________________________________________________________________
;;; FORMATTING
;; <https://github.com/ocaml-ppx/ocamlformat>
(use-package ocamlformat
:ensure t
:after merlin-mode
:hook
(tuareg-mode . ocamlformat-setup-indent))
;; ____________________________________________________________________________
;;; DUNE
;; Major mode for Dune files
(use-package dune
:ensure t)
;; ____________________________________________________________________________
;;; OPAM
;; Use the command "M-x opam-update-env" to select the opam switch for your
;; OCaml project (stolen from Opam's user-setup).
(defun opam-shell-command-to-string (command)
"Similar to shell-command-to-string, but returns nil unless the process
returned 0 and ignores stderr (shell-command-to-string ignores return value)."
(let* ((return-value 0)
(return-string
(with-output-to-string
(setq return-value
(with-current-buffer standard-output
(process-file shell-file-name nil '(t nil) nil
shell-command-switch command))))))
(if (= return-value 0) return-string nil)))
(defun opam-update-env (switch)
"Update the environment to follow current Opam SWITCH configuration."
(interactive
(list
(let ((default
(car (split-string (opam-shell-command-to-string "opam switch show --safe")))))
(completing-read
(concat "opam switch (" default "): ")
(split-string (opam-shell-command-to-string "opam switch list -s --safe") "\n")
nil t nil nil default))))
(let* ((switch-arg (if (= 0 (length switch)) "" (concat "--switch " switch)))
(command (concat "opam config env --safe --sexp " switch-arg))
(env (opam-shell-command-to-string command)))
(when (and env (not (string= env "")))
(dolist (var (car (read-from-string env)))
(setenv (car var) (cadr var))
(when (string= (car var) "PATH")
(setq exec-path (split-string (cadr var) path-separator)))))))
;; Install the basic package selection for convenience
(defun opam-install-dev-packages ()
"Install standard package selection for editor support and development."
(interactive)
(start-process
"opam-install-dev-packages" ; Emacs process name
"*opam-install*" ; Emacs output buffer
"opam" "install" ; shell command
;; Packages
"dune"
"merlin"
"ocaml-lsp-server"
"odoc"
"ocamlformat"
"utop"
"dune-release"
"tuareg"
"omod"
"domainslib"
"user-setup"
"-yes" ; answer "yes" to all questions
))
;; ____________________________________________________________________________
;;; PARENTHESIS DISPLAY
;; Rainbow-delimiters color-coding of nested parens is already enabled
;; for all prog-modes in `ontop-core.el'
(use-package rainbow-delimiters
:ensure t
:hook
(tuareg-interactive-mode . rainbow-delimiters-mode))
;; Make parens styleable, e.g. more or less prominent
;; <https://github.com/tarsius/paren-face>
;; (use-package paren-face
;; :ensure t
;; :hook
;; ((tuareg-mode tuareg-interactive-mode) . paren-face-mode))
;; ____________________________________________________________________________
;;; ORG-MODE BABEL
;; <https://orgmode.org/worg/org-contrib/babel/index.html>
;; Notebook-like literate programming in Emacs
;; Evaluate OCaml code in Org source code blocks via "C-c C-c"
(use-package ob-ocaml
:ensure nil
:custom
(org-babel-ocaml-command "ocaml -nopromptcont"))
(use-package org
:ensure nil
:hook
(org-mode . (lambda ()
(org-babel-do-load-languages
'org-babel-load-languages
(add-to-list 'org-babel-load-languages '(ocaml . t))))))
;; ____________________________________________________________________________
(provide 'ontop-ocaml)
;;; ontop-ocaml.el ends here