(Emacs/config)+ocaml configuration using opam
This commit is contained in:
@@ -44,38 +44,34 @@ Here's an example of some Emacs Lisp code:
|
|||||||
;;; Code:
|
;;; Code:
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
This is an Emacs Lisp code block, something you will see a *LOT* of
|
This allows the document to act as both /source code/ and
|
||||||
throughout. All the Emacs Lisp code blocks in this document are
|
/documentation/ at once. Pretty cool, right? This style of coding is
|
||||||
collected, concatenated then written to a file (=config.el=). This
|
called /literate programming/. Donald Knuth
|
||||||
code file is then evaluated by Emacs
|
|
||||||
[[file:init.el::+literate/load-config][at boot]].
|
|
||||||
|
|
||||||
This style of coding is called /literate programming/. Donald Knuth
|
|
||||||
[[https://en.wikipedia.org/wiki/Literate_programming][really liked]]
|
[[https://en.wikipedia.org/wiki/Literate_programming][really liked]]
|
||||||
the idea. I mainly utilise this to explain my decisions for
|
the idea. I mainly utilise this to explain my decisions for
|
||||||
configuring or using certain packages: Emacs is an opinionated piece
|
configuring or using certain packages: Emacs is an opinionated piece
|
||||||
of software after all.
|
of software and I may as well express my opinions somewhere.
|
||||||
|
|
||||||
Sections tagged =WAIT= are not compiled and are, hence, unused.
|
Sections tagged =WAIT= are not compiled and are, hence, unused.
|
||||||
Usually I provide some reasoning as to why. Such sections can be
|
Usually I provide some reasoning as to why. If using Emacs, the code
|
||||||
easily placed back into the configuration and in Emacs can be loaded
|
in any one section may be loaded interactively (in case I need it
|
||||||
at runtime when I need them, which is why they're not deleted. A lot
|
immediately). A lot of code here is essentially write and forget;
|
||||||
of code here is essentially write and forget; nothing needs to change
|
nothing needs to change unless I find a more efficient way to do
|
||||||
unless I find a more efficient way to do things.
|
things.
|
||||||
|
|
||||||
Some sections border on blog posts justifying why I think they're good
|
Some sections border on blog posts justifying why I think they're good
|
||||||
applications or giving some greater reasoning about my specific
|
applications or giving some greater reasoning about my specific
|
||||||
configuration of a package. That can be distracting, so tangling this
|
configuration of a package. That can be distracting, so reading the
|
||||||
file (via ~(org-babel-tangle)~) and looking at the source code may be
|
produced file may be more helpful for you. Use ~org-babel-tangle~ in
|
||||||
more helpful.
|
Emacs to do so, or ask me very nicely.
|
||||||
* Basics
|
* Basics
|
||||||
Let's setup a few things:
|
Let's setup a few absolute essentials:
|
||||||
+ My name and mail address
|
+ My name and mail address
|
||||||
+ File encoding
|
+ File encoding (no "\r" characters at the end of lines, please)
|
||||||
+ Backup files (~backup-directory-alist~)
|
+ Where to store backup files (~backup-directory-alist~)
|
||||||
+ Refreshing buffers when a change occurs (~auto-revert-mode~)
|
+ Auto refresh buffers when a change occurs (~auto-revert-mode~)
|
||||||
+ Yes or no questions being less painful (~y-or-n-p~)
|
+ Yes or no questions can be less painful (~y-or-n-p~)
|
||||||
+ Make the kill ring work seamlessly with the clipboard
|
+ Make the "kill ring" work seamlessly with the clipboard
|
||||||
|
|
||||||
#+begin_src emacs-lisp
|
#+begin_src emacs-lisp
|
||||||
(use-package emacs
|
(use-package emacs
|
||||||
@@ -102,8 +98,8 @@ throughout the file.
|
|||||||
An anonymous function (~lambda~) which takes no arguments is a
|
An anonymous function (~lambda~) which takes no arguments is a
|
||||||
procedure. This macro generates procedures, with the parameters of
|
procedure. This macro generates procedures, with the parameters of
|
||||||
the macro being the body of the procedure. It returns it in quoted
|
the macro being the body of the procedure. It returns it in quoted
|
||||||
form, as that is the most common use of this macro.
|
form (as data rather than code), as that is the most common use of
|
||||||
|
this macro.
|
||||||
#+begin_src emacs-lisp
|
#+begin_src emacs-lisp
|
||||||
(defmacro proc (&rest BODY)
|
(defmacro proc (&rest BODY)
|
||||||
"For a given list of forms BODY, return a quoted 0 argument
|
"For a given list of forms BODY, return a quoted 0 argument
|
||||||
@@ -112,13 +108,14 @@ lambda."
|
|||||||
#+end_src
|
#+end_src
|
||||||
** Automatically run a command on saving
|
** Automatically run a command on saving
|
||||||
Define a macro which creates hooks into ~after-save-hook~. On certain
|
Define a macro which creates hooks into ~after-save-hook~. On certain
|
||||||
~conditions~ being met, ~to-run~ is evaluated.
|
~conditions~ being met, ~to-run~ is evaluated. Classic example use
|
||||||
|
case: compiling on save.
|
||||||
|
|
||||||
#+begin_src emacs-lisp
|
#+begin_src emacs-lisp
|
||||||
(use-package simple
|
(use-package simple
|
||||||
:defer t
|
:defer t
|
||||||
:config
|
:config
|
||||||
(defmacro +oreo/create-auto-save (conditions &rest to-run)
|
(defmacro create-auto-save (conditions &rest to-run)
|
||||||
"Create a hook for after saves, where on CONDITIONS being met
|
"Create a hook for after saves, where on CONDITIONS being met
|
||||||
TO-RUN is evaluated."
|
TO-RUN is evaluated."
|
||||||
`(add-hook 'after-save-hook
|
`(add-hook 'after-save-hook
|
||||||
@@ -126,56 +123,51 @@ TO-RUN is evaluated."
|
|||||||
(interactive)
|
(interactive)
|
||||||
(when ,conditions ,@to-run)))))
|
(when ,conditions ,@to-run)))))
|
||||||
#+end_src
|
#+end_src
|
||||||
** Setting number of native jobs
|
|
||||||
In [[file:early-init.el][early-init.el]] I set the number of
|
|
||||||
native-workers to 4, which isn't necessarily optimal when
|
|
||||||
loading/compiling the rest of this file depending on the machine I
|
|
||||||
use:
|
|
||||||
- On my laptop (=newboy=) I'd prefer to have it use 2-3 threads so
|
|
||||||
I can actually use the rest of the laptop while waiting for
|
|
||||||
compilation
|
|
||||||
- On my desktop (=oldboy=) I'd prefer to use 4-6 threads as I can
|
|
||||||
afford more, so I can get a faster load up.
|
|
||||||
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package comp
|
|
||||||
:init
|
|
||||||
(setq native-comp-async-jobs-number
|
|
||||||
(pcase (system-name)
|
|
||||||
("newboy" 3)
|
|
||||||
("oldboy" 6)
|
|
||||||
(_ 3))))
|
|
||||||
#+end_src
|
|
||||||
** Clean buffer list
|
** Clean buffer list
|
||||||
Clean all buffers except for those in ~+oreo/keep-buffers~.
|
Clean all buffers except for those in ~clean-buffers-keep~.
|
||||||
|
|
||||||
#+begin_src emacs-lisp
|
#+begin_src emacs-lisp
|
||||||
(defconst +oreo/keep-buffers
|
(defconst clean-buffers-keep
|
||||||
(list "config.org" "*scratch*"
|
(list "config.org" "*scratch*"
|
||||||
"*dashboard*" "*Messages*"
|
"*dashboard*" "*Messages*"
|
||||||
"*Warnings*" "*eshell*")
|
"*Warnings*" "*eshell*")
|
||||||
"List of buffer names to preserve.")
|
"List of buffer names to preserve.")
|
||||||
|
|
||||||
(defun +oreo/clean-buffers ()
|
(defun clean-buffers ()
|
||||||
"Kill all buffers except any with names in +oreo/keep-buffers."
|
"Kill all buffers except any with names in CLEAN-BUFFERS-KEEP."
|
||||||
(interactive)
|
(interactive)
|
||||||
(let ((should-not-kill #'(lambda (buf) (member (buffer-name buf)
|
(let ((should-not-kill
|
||||||
+oreo/keep-buffers))))
|
#'(lambda (buf)
|
||||||
(mapcar #'kill-buffer (cl-remove-if should-not-kill (buffer-list)))))
|
(member (buffer-name buf)
|
||||||
|
clean-buffers-keep))))
|
||||||
|
(mapc #'kill-buffer
|
||||||
|
(cl-remove-if should-not-kill (buffer-list)))))
|
||||||
#+end_src
|
#+end_src
|
||||||
** Custom window management
|
** Custom window management
|
||||||
Emacs' default window management is horrendous, using other windows on
|
Generally speaking, applications that have a windowing concept do not
|
||||||
a whim as if your carefully crafted window setup doesn't exist!
|
have a lot of options for how those windows are placed. Emacs has a
|
||||||
Thankfully you can change this behaviour via the
|
window management system unlike any other piece of software I have
|
||||||
~display-buffer-alist~ which matches regular expressions on buffer
|
ever used, with some complexity but incredible capability.
|
||||||
names with a set of properties and functions that dictate how the
|
|
||||||
window for a buffer should be displayed. It's a bit verbose but once
|
|
||||||
you get the hang of it it's actually really unique.
|
|
||||||
|
|
||||||
Here I add a use-package keyword to make ~display-buffer-alist~
|
The big idea is this table (=alists= are basically just tables),
|
||||||
records within a single use-package call instead of doing the
|
~display-buffer-alist~, which associates regular expressions with
|
||||||
~add-to-list~ yourself. I have no idea whether it's optimal AT ALL,
|
"actions". The regular expressions are for the name of buffers, and
|
||||||
but it works for me.
|
the actions are how the buffer should be displayed. And there are a
|
||||||
|
*lot* of ways to display buffers.
|
||||||
|
|
||||||
|
Here's an example record:
|
||||||
|
#+begin_src lisp
|
||||||
|
'("config.org"
|
||||||
|
(display-buffer-in-side-window)
|
||||||
|
(side . bottom))
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
This matches any buffer named =config.org=, displaying the buffer in a
|
||||||
|
side window to the bottom.
|
||||||
|
|
||||||
|
What I configure here is a ~use-package~ keyword, ~:display~, which
|
||||||
|
will allow me to write associations to ~display-buffer-alist~ really
|
||||||
|
easily.
|
||||||
|
|
||||||
2024-04-23: Found this option ~switch-to-buffer-obey-display-actions~
|
2024-04-23: Found this option ~switch-to-buffer-obey-display-actions~
|
||||||
which makes manual buffer switches obey the same constraints via
|
which makes manual buffer switches obey the same constraints via
|
||||||
@@ -230,22 +222,32 @@ do that for me.
|
|||||||
(cl-loop for element in elements
|
(cl-loop for element in elements
|
||||||
collect `(cl-pushnew ,element ,listvar))))
|
collect `(cl-pushnew ,element ,listvar))))
|
||||||
#+end_src
|
#+end_src
|
||||||
|
** Setting number of native jobs
|
||||||
|
Emacs has a native compilation capability to make things /even
|
||||||
|
faster/. In [[file:early-init.el][early-init.el]] I set the number of
|
||||||
|
native-workers to 4, which isn't necessarily optimal when
|
||||||
|
loading/compiling the rest of this file depending on the machine I
|
||||||
|
use. On my machines, which have 8 process throughput (4 core +
|
||||||
|
hyperthreading), 6-7 workers makes much more sense. On a machine I've
|
||||||
|
never used before, 3 seems to be a reasonable default.
|
||||||
|
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package comp
|
||||||
|
:init
|
||||||
|
(setq native-comp-async-jobs-number
|
||||||
|
(pcase (system-name)
|
||||||
|
((or "ravenmaiden" "oldboy") 6)
|
||||||
|
(_ 3))))
|
||||||
|
#+end_src
|
||||||
* Aesthetics
|
* Aesthetics
|
||||||
General look and feel of Emacs (mostly disabling stuff I don't like).
|
General look and feel of Emacs (mostly disabling stuff I don't like).
|
||||||
** Themes
|
** Themes
|
||||||
I have both a dark and light theme for differing situations.
|
I have both a dark and light theme for differing situations. Here I
|
||||||
Here I configure which one to load and how to switch between them.
|
configure a timer which ensures I have a light theme during the day
|
||||||
|
and dark theme at night. I wrote my own themes by copying stuff I
|
||||||
My preferred dark theme is my own "personal-solarized" theme which is
|
like from other themes then modifying them. The dark theme is in
|
||||||
stored in the Emacs lisp folder (look at
|
[[file:elisp/personal-solarized-theme.el][this file]] and the light
|
||||||
[[file:elisp/personal-solarized-theme.el][this file]]). It's
|
theme is in [[file:elisp/personal-light-theme.el][this file]].
|
||||||
essentially a copy of the solarized theme (from the ~solarized-themes~
|
|
||||||
package) with a few personal changes.
|
|
||||||
|
|
||||||
My preferred light theme is modus-operandi, mostly because I don't
|
|
||||||
care to design my own light theme and I rarely use them. They are
|
|
||||||
necessary in high light situations where a dark mode would strain the
|
|
||||||
eyes too much.
|
|
||||||
|
|
||||||
#+begin_src emacs-lisp
|
#+begin_src emacs-lisp
|
||||||
(use-package custom
|
(use-package custom
|
||||||
@@ -3748,6 +3750,59 @@ fun to write programs in.
|
|||||||
"sr" #'scheme-send-region
|
"sr" #'scheme-send-region
|
||||||
"e" #'scheme-send-last-sexp))
|
"e" #'scheme-send-last-sexp))
|
||||||
#+end_src
|
#+end_src
|
||||||
|
** Ocaml
|
||||||
|
*** Ocaml Setup
|
||||||
|
Firstly, install ~opam~ and ~ocaml~. Then run the following script:
|
||||||
|
#+begin_src sh
|
||||||
|
opam install tuareg ocamlformat odoc utop merlin user-setup
|
||||||
|
opam user-setup install
|
||||||
|
mv ~/.emacs.d/opam-user-setup.el ~/.config/emacs/elisp
|
||||||
|
rm -rf ~/.emacs.d ~/.emacs
|
||||||
|
#+end_src
|
||||||
|
This sets up the necessary packages (particularly Emacs Lisp) and some
|
||||||
|
configuration that ensures Emacs is consistent with the user
|
||||||
|
installation. Notice the moving of =opam-user-setup.el= into
|
||||||
|
=~/.config/emacs/elisp=, which we'll use to setup the ocaml
|
||||||
|
experience.
|
||||||
|
*** Ocaml Configuration
|
||||||
|
Here I load the =opam-user-setup= package setup earlier, with some
|
||||||
|
neat tips from the default =~/.emacs= generated by ~opam user-setup
|
||||||
|
install~.
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package opam-user-setup
|
||||||
|
:defer t
|
||||||
|
:load-path "elisp/"
|
||||||
|
:mode ("\\.ml" . tuareg-mode)
|
||||||
|
:display
|
||||||
|
("\\*utop\\*"
|
||||||
|
(display-buffer-at-bottom)
|
||||||
|
(window-height . 0.3))
|
||||||
|
:config
|
||||||
|
(add-to-list 'compilation-error-regexp-alist-alist
|
||||||
|
`(ocaml
|
||||||
|
"[Ff]ile \\(\"\\(.*?\\)\", line \\(-?[0-9]+\\)\\(, characters \\(-?[0-9]+\\)-\\([0-9]+\\)\\)?\\)\\(:\n\\(\\(Warning .*?\\)\\|\\(Error\\)\\):\\)?"
|
||||||
|
2 3 (5 . 6) (9 . 11) 1 (8 compilation-message-face)))
|
||||||
|
(add-to-list 'compilation-error-regexp-alist
|
||||||
|
'ocaml)
|
||||||
|
:general
|
||||||
|
(local-leader
|
||||||
|
:keymaps 'tuareg-mode-map
|
||||||
|
"u" #'utop)
|
||||||
|
(local-leader
|
||||||
|
:keymaps 'tuareg-mode-map
|
||||||
|
:infix "e"
|
||||||
|
"r" #'utop-eval-region
|
||||||
|
"e" #'utop-eval-phrase
|
||||||
|
"b" #'utop-eval-buffer))
|
||||||
|
|
||||||
|
(use-package merlin-eldoc
|
||||||
|
:straight t
|
||||||
|
:after opam-user-setup
|
||||||
|
:hook
|
||||||
|
(tuareg-mode-hook . merlin-eldoc-setup)
|
||||||
|
:init
|
||||||
|
(setq merlin-eldoc-occurrences nil))
|
||||||
|
#+end_src
|
||||||
** Common Lisp
|
** Common Lisp
|
||||||
Common Lisp is a dialect of Lisp, the most /common/ one around. Emacs
|
Common Lisp is a dialect of Lisp, the most /common/ one around. Emacs
|
||||||
comes with builtin Lisp support, of course, and it's really good in
|
comes with builtin Lisp support, of course, and it's really good in
|
||||||
|
|||||||
Reference in New Issue
Block a user