leverage use-package’s :hook parameter
the inspiration for trying an org-mode literate Emacs file came from here: https://github.com/r0man/.emacs.d
this is an opinionated yet minimal setup for someone who wants to try using Emacs for writing OCaml or ReasonML code. One defining characteristic of this project is the fact that it works with Merlin from both opam and reason-cli, and it chooses the version based off of the type of file.
note: this could cause an issue for people who write bucklescript, as that code will be checked against opam’s merlin and not the one from reason-cli. I am unsure of how to tell the difference between bucklescript and ocaml code programmatically.
to use this you must:
- install merlin and ocp-indent:
opam install merlin ocp-indent
- install reason-cli via npm/yarn:
npm install -g reason-cli@3.1.0-linux
ornpm install -g reason-cli@3.1.0-darwin
Keybinding | Module | Function |
---|---|---|
C-h k | help | describe-key |
M-<right> | auto-highlight-symbol | next occurrence |
M-<left> | helm | previous occurrence |
C-c b | helm | helm-filtered-bookmarks |
C-x C-f | helm | helm-find-files |
C-x f | helm | helm-find-files |
C-x b | helm | helm-mini |
C-x C-b | helm | helm-buffers-list |
C-h f | helm | helm-apropos |
C-h r | helm | helm-info-emacs |
C-h C-l | helm | helm-locate-library |
C-c f | helm | helm-recentf |
C-h SPC | helm | helm-all-mark-rings |
C-c h x | helm | helm-register |
M-y | helm | helm-show-kill-ring |
M-x | helm | helm-M-x |
C-c C-l | helm | helm-minibuffer-history |
C-s | helm-swoop | helm-swoop-without-pre-input |
C-r | helm-swoop | helm-previous-line |
C-s | helm-swoop | helm-next-line |
M-. | merlin | merlin-locate |
M-, | merlin | merlin-pop-stack |
C-c C-o | merlin | merlin-occurrences |
C-c C-j | merlin | merlin-jump |
C-c i | merlin | merlin-locate-ident |
C-c C-e | merlin | merlin-edit-occurrences |
use Common Lisp features in Emacs Lisp.
(eval-when-compile
(require 'cl)
(require 'cl-lib))
make it so that (,{,[, and ” are auto paired.
(electric-pair-mode t)
make the highlighting/region selected easier to see.
(set-face-attribute 'region nil :background "green")
make the default yes/no y/n instead
(fset 'yes-or-no-p 'y-or-n-p)
make elisp bearable
(if (fboundp 'paren-set-mode)
(paren-set-mode 'sexp)
(defvar show-paren-style)
(setq show-paren-style 'expression)
(show-paren-mode t))
(add-hook 'emacs-lisp-mode-hook 'turn-on-eldoc-mode)
use utf-8 everywhere!
(prefer-coding-system 'utf-8)
(set-terminal-coding-system 'utf-8)
(set-language-environment "UTF-8")
(set-default-coding-systems 'utf-8)
(setq buffer-file-coding-system 'utf-8)
two performance speedups
- https://emacs.stackexchange.com/questions/28736/emacs-pointcursor-movement-lag/28746
- https://www.reddit.com/r/emacs/comments/2dgy52/how_to_stop_emacs_automatically_recentering_the/
(setq auto-window-vscroll nil)
(setq scroll-step 1)
make it so that links in Emacs are clickable
(define-globalized-minor-mode global-goto-address-mode goto-address-mode goto-address-mode)
(global-goto-address-mode)
automatically maximize the Emacs window
(add-to-list 'default-frame-alist '(fullscreen . maximized))
use secure defaults
(setq network-security-level 'high)
font-lock is enabled by default, but make sure it is globally applied
(global-font-lock-mode 1)
this turns certain symbols into fonts (lambda -> λ)
(global-prettify-symbols-mode 1)
change the window title to something useful
(setq frame-title-format '(buffer-file-name "%f" "%b"))
turn of the ring-bell functionality
(setq ring-bell-function 'ignore)
start wrapping at 80 for text files
(use-package auto-dictionary
:ensure t
:init (add-hook 'flyspell-mode-hook (lambda () (auto-dictionary-mode 1))))
(add-hook 'text-mode-hook
'(lambda()
(turn-on-auto-fill)
(set-fill-column 80)
(setq-default auto-fill-function 'do-auto-fill)))
use a dictionary! enable flyspell in text mode
(defun enable-flyspell-mode ()
"Enable Flyspell mode."
(flyspell-mode 1))
(dolist (hook '(text-mode-hook))
(add-hook hook 'enable-flyspell-mode))
enable flyspell in programming mode
(defun enable-flyspell-prog-mode ()
"Enable Flyspell Programming mode."
(flyspell-prog-mode))
(dolist (hook '(prog-mode-hook))
(add-hook hook 'enable-flyspell-prog-mode))
don’t print messages when checking words.
(setq flyspell-issue-message-flag nil)
use sane defaults for org-mode follow 80-character word-wrap rule also allow for highlighting languages within org-mode using org-babel
(use-package org
:ensure t
:defer 1
:mode ("\\.org\\'" . org-mode)
:config
(add-hook 'org-mode-hook
'(lambda()
(turn-on-auto-fill)
(set-fill-column 80)
(setq-default auto-fill-function 'do-auto-fill)))
(require 'ob-clojure)
(setq org-babel-clojure-backend 'cider)
(setq org-src-fontify-natively t)
(org-babel-do-load-languages
'org-babel-load-languages
'((clojure . t)
(lisp . t)
(emacs-lisp . t)
(haskell . t)
(latex . t)
(ledger . t)
(ocaml . t)
(python . t)
(sh . t)
(sql . t))))
automatically get the path for binaries on the current system
(if (string-equal system-type "windows-nt")
(progn
(setenv "PATH" (concat
"C:\\Program Files\\Git\\usr\\bin" ";" ;; Unix tools
(getenv "PATH"))))
(progn
(use-package exec-path-from-shell
:ensure t
:config
(when (memq window-system '(mac ns x))
(exec-path-from-shell-initialize)))))
setup helm and make the global keybindings also setup helm-swoop
(use-package helm
:ensure t
:config
(helm-mode 1)
(helm-popup-tip-mode 1)
(helm-autoresize-mode t)
(setq helm-autoresize-min-height 40)
(setq helm-M-x-fuzzy-match t)
(setq helm-buffers-fuzzy-matching t)
(setq helm-recentf-fuzzy-match t)
(setq helm-lisp-fuzzy-completion t)
(setq helm-mini-default-sources '(helm-source-buffers-list
helm-source-recentf
helm-source-bookmarks
helm-source-buffer-not-found))
(require 'helm-eshell)
(add-hook 'eshell-mode-hook
#'(lambda ()
(define-key eshell-mode-map (kbd "M-l") 'helm-eshell-history)))
;; (define-key helm-map (kbd "<return>") 'helm-execute-persistent-action)
;; (global-set-key (kbd "C-s") #'helm-occur) ; using helm-swoop now
(global-set-key (kbd "C-c b") #'helm-filtered-bookmarks)
(global-set-key (kbd "C-c C-b") #'helm-filtered-bookmarks) ; because I am an idiot
(global-set-key (kbd "C-x C-f") #'helm-find-files)
(global-set-key (kbd "C-x b") #'helm-mini)
(global-set-key (kbd "C-x C-b") 'helm-buffers-list)
(global-set-key (kbd "C-h f") 'helm-apropos)
(global-set-key (kbd "C-h r") 'helm-info-emacs)
(global-set-key (kbd "C-h C-l") 'helm-locate-library)
(global-set-key (kbd "C-c f") 'helm-recentf)
(global-set-key (kbd "C-h SPC") 'helm-all-mark-rings)
(global-set-key (kbd "C-c h x") 'helm-register)
(global-set-key (kbd "M-y") 'helm-show-kill-ring)
(global-set-key (kbd "M-x") #'helm-M-x)
(define-key minibuffer-local-map (kbd "C-c C-l") 'helm-minibuffer-history)
(define-key helm-map [backspace] #'backward-kill-word))
(use-package helm-swoop
:ensure t
:config
(global-set-key (kbd "C-s") 'helm-swoop-without-pre-input)
(define-key helm-swoop-map (kbd "C-r") 'helm-previous-line)
(define-key helm-swoop-map (kbd "C-s") 'helm-next-line))
use company-mode (not auto-complete) for text-completion
(use-package company
:ensure t
:config
(add-hook 'prog-mode-hook 'company-mode)
(setq company-dabbrev-downcase 0)
(setq company-idle-delay 0))
(use-package company-quickhelp
:ensure t
:config
(company-quickhelp-mode 1)
(define-key company-active-map (kbd "C-c h") #'company-quickhelp-manual-begin))
remember that ocp-indent has to be installed via opam!
(let ((opam-share (ignore-errors (car (process-lines "opam" "config" "var" "share")))))
(when (and opam-share (file-directory-p opam-share))
(add-to-list 'load-path (expand-file-name "emacs/site-lisp" opam-share))))
(use-package ocp-indent)
(use-package tuareg
:ensure t
:config
(add-hook 'before-save-hook 'ocp-indent-buffer nil t)
(setq auto-mode-alist
(append '(("\\.ml[ily]?$" . tuareg-mode)
("\\.topml$" . tuareg-mode))
auto-mode-alist)))
setup reasonml support and allow for setting merlin-commmand to the reason-cli merlin
(defun shell-cmd (cmd)
"Returns the stdout output of a shell command or nil if the command returned
an error"
(car (ignore-errors (apply 'process-lines (split-string cmd)))))
(quelpa '(reason-mode :repo "reasonml-editor/reason-mode" :fetcher github :stable t))
(use-package reason-mode
:config
(let* ((refmt-bin (shell-cmd "which refmt")))
(when refmt-bin
(setq refmt-command refmt-bin)))
(add-hook
'reason-mode-hook
(lambda ()
(add-hook 'before-save-hook 'refmt-before-save nil t)
(setq-local merlin-command (shell-cmd "which ocamlmerlin"))
(merlin-mode))))
setup merlin to default to opam and add hooks for reason/tuareg/caml-mode
(use-package merlin
:custom
(merlin-command 'opam)
(merlin-completion-with-doc t)
(company-quickhelp-mode t)
:config
(autoload 'merlin-mode "merlin" nil t nil)
:bind (:map merlin-mode-map
("M-." . merlin-locate)
("M-," . merlin-pop-stack)
("C-c C-o" . merlin-occurrences)
("C-c C-j" . merlin-jump)
("C-c i" . merlin-locate-ident)
("C-c C-e" . merlin-iedit-occurrences))
:hook
;; Start merlin on ml files
(reason-mode . merlin-mode)
(tuareg-mode . merlin-mode)
(caml-mode-hook . merlin-mode))
dynamically choose utop version (opam’s vs reason-cli’s) based on buffer
(defun reason/rtop-prompt ()
"The rtop prompt function."
(let ((prompt (format "rtop[%d]> " utop-command-number)))
(add-text-properties 0 (length prompt) '(face utop-prompt) prompt)
prompt))
(use-package utop
:config
(autoload 'utop "utop" "Toplevel for OCaml" t)
(autoload 'utop-minor-mode "utop" "Minor mode for utop" t)
(defun utop-opam-utop () (progn
(setq-local utop-command "opam config exec -- utop -emacs")
utop-minor-mode))
(defun utop-reason-cli-rtop () (progn
(setq-local utop-command (concat (shell-cmd "which rtop") " -emacs"))
(setq-local utop-prompt 'reason/rtop-prompt)
utop-minor-mode))
:hook
(tuareg-mode . utop-opam-utop)
(reason-mode . utop-reason-cli-rtop))
right now flycheck-ocaml only supports ocaml, but hopefully it will be extended to reason as well
(use-package flycheck
:ensure t
:config
(global-flycheck-mode))
(use-package flycheck-popup-tip
:ensure t
:config
(flycheck-popup-tip-mode))
(use-package flycheck-ocaml
:ensure t
:config
(add-hook 'tuareg-mode-hook
(lambda ()
;; disable Merlin's own error checking
(setq-local merlin-error-after-save nil)
;; enable Flycheck checker
(flycheck-ocaml-setup))))