To increase readability, I’ll try to unify how things are generally structured.
I’d like to move away from the cramped Lisp I’ve been writing to something I term a “relaxed” style. It’s important to let code breathe. A blank line can do a lot for readability.
I personally still favor longer functions over a collection of many one-use functions. But usually there’s a certain flow to a function, there’s a topography: this is where we are now, this is where we’ll get to.
If the code after keywords serves more than a single goal, it should be grouped and each group separated by an empty line. Ideally, these groups should also have a comment if the purpose isn’t immediately clear.
This mostly applies to :init
and :config
blocks but can also make
sense for long :custom
and :bind
blocks.
Keyword :demand
might skip the t
if an :after
immediately follows, in
that case they should also not be separated by a line.
The order is:
- Include
if
wal-ways
- Load
ensure
load-path
defer
ordemand
after
commands
mode
hook
- Configuration
init
config
custom
- Bindings
bind-keymap
bind
wal-bind
general
- Delighting/Diminishing
delight
diminish
There’s no hard and fast rule there, but generally, only values 1
, 2
and 3
should be used.
1
is for semi-important stand-alone packages or extensions; 2
for
useful extensions, and 3
for nice-to-have extensions.
For example, the linters of language packages should use 1
, their test
packages 2
.
A letter may prefix an action or a nested keymap.
Commits are linted using commitlint
. To set this up, load file
setup/wal-setup.el
and select commit hooks
.
The rules are defined in commitlint.config.js
and a description of all
rules can be found here.
Each major section has a header block that contains the following in the following order:
declare-function
and emptydefvar
defgroup
defcustom
The order of declarations in subsections is:
defvar
defconst
defmacro
define-minor-mode
defun
defalias
use-package
- other function calls
The value of a variable should aim to be (or start) on the first line.
(defcustom some-variable "value"
"Doc string."
:type 'string
:group 'some-group)
(defvar func-variable (expand-file-name "hello" "~")
"Doc string.")
(defcustom some-list-variable '(a
list
of
items)
"Doc string."
:type '(repeat symbol)
:group 'some-group)
(defconst sexp-variable (let ((var "hello"))
var)
"Doc string.")
Advising functions should follow a common template.
Combinator | Template |
---|---|
:after | wal-then-<do> |
:around | wal-with{out}-<do> |
:before | wal-first-<do> |
:after-until | wal-otherwise-<do> |
:before-while | wal-ignore-if-<sth> |
:before-until | wal-in-case-of-<sth>-<do> |
:filter-args | wal-pick-<sth> |
:filter-return | wal-adjust-by-<sth> |
:override | wal-instead-<do> |
Example for advising :around
.
(defun wal-with-big-vertico (fun &rest args)
"Call FUN with ARGS but increase the `vertico-count'."
(defvar vertico-count)
(let ((vertico-count 20))
(apply fun args)))
(advice-add
'consult-ripgrep :around
#'wal-with-big-vertico)
;;; wal-config.el --- Walheimat's literate Emacs configuration. -*- lexical-binding: t -*-
;; Version: 2.3.1
;; Package-Requires: ((emacs "28.1"))
;;; Commentary:
;;
;; Require all sub-packages.
;;; Code:
(eval-when-compile
(require 'wal-useful nil t)
(require 'wal-visuals nil t))
(declare-function consult-org-heading "ext:consult.el")
(declare-function magit-diff-range "ext:magit.el")
(declare-function posframe-delete "ext:posframe.el")
(declare-function posframe-show "ext:posframe.el")
(declare-function project-buffers "ext:project.el")
(declare-function transient-prefix "ext:transient.el")
(declare-function transient-setup "ext:transient.el")
(declare-function wal-async-process "wal-useful.el")
(declare-function wal-flycheck-file "wal-fix.el")
(declare-function wal-flycheck-file--erase "wal-fix.el")
(declare-function wal-flycheck-file--get-buffer "wal-fix.el")
(declare-function wal-tangle-library "wal.el")
(declare-function wal-transparency--param "wal-visuals.el")
(defvar vertico-preselect)
(defvar wal-default-path)
(defvar wal-emacs-config-build-path)
(defgroup wal-config nil
"Customize configuring the packages."
:group 'wal
:tag "Configuration")
(defcustom wal-config-show-animation t
"Whether to show an animated whale while editing the config."
:type 'boolean
:group 'wal-config)
(defcustom wal-config-animation-type 'blue
"The whale to use in the config animation."
:type '(choice (const :tag "Blue whale" blue)
(const :tag "Cachalot" cachalot))
:group 'wal-config)
Animate a swimming whale in a posframe
.
(defvar wal-config-animation--fins (list "-" "'" "-" ","))
(defun wal-config-animation--build-key-frames (pattern)
"Build key frames using PATTERN."
(seq--into-vector
(seq-map (lambda (it)
(format pattern it))
wal-config-animation--fins)))
(defvar wal-config-animation--cachalot-key-frames
(wal-config-animation--build-key-frames "(__.%s >{"))
(defvar wal-config-animation--blue-whale-key-frames
(wal-config-animation--build-key-frames "⎝ ﬞ %s {"))
(defvar wal-config-animation-key-frames nil)
(defvar wal-config-animation-frame-index 0)
(defvar wal-config-animation-animation-speed 0.4)
(defvar wal-config-animation-buffer "*swimming-whale*")
(defvar wal-config-animation-timer nil)
(defvar-local wal-config-animation-indirect-buffer nil)
(defvar-local wal-config-animation-parent-buffer nil)
(defvar wal-config-animation--ignored-buffers '("COMMIT_EDITMSG")
"List of buffer names to ignore.")
(defun wal-config-animation-animate ()
"Animate the ASCII whale."
(with-current-buffer (get-buffer-create wal-config-animation-buffer)
;; Clear.
(erase-buffer)
;; Render current frame.
(let* ((frame (aref wal-config-animation-key-frames wal-config-animation-frame-index))
(colored (propertize frame 'face `(:background ,(face-attribute 'default :background)
:foreground ,(face-attribute 'cursor :background)))))
(insert colored)
;; Advance to the next frame.
(setq wal-config-animation-frame-index
(mod
(1+ wal-config-animation-frame-index)
(length wal-config-animation-key-frames))))))
(defun wal-config-animation--start-animation ()
"Start the animation.
No-op if it is already running."
(unless wal-config-animation-timer
;; Set up key frames.
(setq wal-config-animation-key-frames
(pcase wal-config-animation-type
('cachalot wal-config-animation--cachalot-key-frames)
('blue wal-config-animation--blue-whale-key-frames)
(_ wal-config-animation--blue-whale-key-frames)))
;; Make sure the first frame is animated before we display.
(wal-config-animation-animate)
;; Start timer.
(setq wal-config-animation-timer (run-with-timer
0
wal-config-animation-animation-speed
#'wal-config-animation-animate))))
(defun wal-config-animation--stop-animation ()
"Stop the animation if it is running.
Will not do anything if there are still buffers who display the
whale."
(when (and wal-config-animation-timer
(not (seq-find
(lambda (it) (buffer-local-value 'wal-config-animation-parent-buffer it))
(buffer-list))))
(cancel-timer wal-config-animation-timer)
(setq wal-config-animation-timer nil)
(kill-buffer wal-config-animation-buffer)))
(defun wal-config-animation-setup ()
"Set up the animated whale."
;; Queue up timer if it isn't running.
(wal-config-animation--start-animation)
;; Set this buffer as the parent and create an indirect buffer of
;; the animation buffer.
(setq wal-config-animation-parent-buffer (current-buffer)
wal-config-animation-indirect-buffer (make-indirect-buffer
(get-buffer wal-config-animation-buffer)
(generate-new-buffer-name wal-config-animation-buffer)))
;; Set font size for indirect buffer.
(wal-config-animation--set-font-height)
;; Set up hooks to clean up and re-display.
(add-hook 'wal-fonts-updated-hook #'wal-config-animation--reset nil t)
(add-hook 'kill-buffer-hook #'wal-config-animation-clean-up nil t)
(add-hook 'window-configuration-change-hook #'wal-config-animation-display nil t))
(defun wal-config-animation--set-font-height ()
"Set font height to double of `wal-fixed-font-height'."
(defvar wal-fixed-font-height)
(with-current-buffer wal-config-animation-indirect-buffer
(let ((fheight (* 2 wal-fixed-font-height)))
(face-remap-add-relative 'default :height fheight))))
(defun wal-config-animation--reset ()
"Reset the animation."
(wal-config-animation--set-font-height)
(wal-config-animation-display))
(defun wal-config-animation-clean-up ()
"Clean up the animation."
;; Delete the buffer.
(when wal-config-animation-indirect-buffer
(posframe-delete wal-config-animation-indirect-buffer))
(setq wal-config-animation-parent-buffer nil
wal-config-animation-indirect-buffer nil)
;; Remove animation and re-positioning hooks.
(remove-hook 'wal-fonts-updated-hook #'wal-config-animation--reset t)
(remove-hook 'kill-buffer-hook #'wal-config-animation-clean-up t)
(remove-hook 'window-configuration-change-hook #'wal-config-animation-display t)
;; Maybe cancel the timer.
(wal-config-animation--stop-animation))
(defun wal-config-animation-poshandler (info)
"Position handler for ASCII whale.
INFO contains positioning information."
(let* ((window-left (plist-get info :parent-window-left))
(window-top (plist-get info :parent-window-top))
(window-width (plist-get info :parent-window-width))
(posframe-width (plist-get info :posframe-width))
;; Offset the frame, taking the pixel-height of a line into
;; account.
(p-window (plist-get info :parent-window))
(p-line-height (with-selected-window p-window (line-pixel-height)))
(offset-x p-line-height)
(offset-y p-line-height))
(cons (- (+ window-left window-width
(- 0 posframe-width))
offset-x)
(+ window-top offset-y))))
(defun wal-config-animation-hidehandler (info)
"Check INFO whether the parent buffer is invisible."
(and-let* ((parent (cdr (plist-get info :posframe-parent-buffer)))
(invisible (not (get-buffer-window parent t))))))
(defun wal-config-animation-display ()
"Display the running animation in a posframe."
(when (require 'posframe nil t)
(let ((default-frame-alist nil)
(frame (posframe-show
wal-config-animation-indirect-buffer
:accept-focus nil
:poshandler 'wal-config-animation-poshandler
:posframe-parent-buffer wal-config-animation-parent-buffer
:hidehandler 'wal-config-animation-hidehandler)))
(when (eq 'alpha-background (wal-transparency--param))
(modify-frame-parameters frame `((alpha-background . ,wal-transparency)))))))
(defun wal-config-animation--maybe-display (buffer &optional no-record &rest _args)
"Maybe display the animation for BUFFER.
This only happens for project buffers as long as NO-RECORD is not t."
(when wal-config-show-animation
(and-let* (((not no-record))
(buffer (get-buffer buffer))
((not (and-let* ((name (buffer-file-name buffer)))
(seq-find (lambda (it) (string-match-p it name)) wal-config-animation--ignored-buffers))))
((null (buffer-local-value 'wal-config-animation-parent-buffer buffer)))
((memq buffer (project-buffers (project-current nil wal-default-path)))))
(wal-config-animation-setup)
(wal-config-animation-display))))
(defun wal-config-animation--on-find-file ()
"Maybe display animation for new file."
(wal-config-animation--maybe-display (current-buffer)))
(wal-on-boot config
(advice-add 'switch-to-buffer :after #'wal-config-animation--maybe-display)
(add-hook 'find-file-hook #'wal-config-animation--on-find-file))
Minor mode for editing this config.
(defvar wal-config-mode-map (make-sparse-keymap)
"Map for `wal-config-mode'.")
(defun wal-describe-config-version ()
"Describe the config's version.
This returns the tag and its annotation as propertized strings."
(interactive)
(let* ((default-directory wal-default-path)
(version (propertize
(string-trim
(shell-command-to-string "git describe --abbrev=0"))
'face 'bold))
(cat (propertize
(string-trim
(shell-command-to-string (format "git cat-file tag %s" version)))
'face 'italic))
(out (concat version ": " (car (last (string-lines cat))))))
(cond
(noninteractive out)
(t (message out)))))
(defun wal-show-config-diff-range ()
"Call `magit-diff-range' with the latest tag."
(interactive)
(let ((version (shell-command-to-string "git describe --abbrev=0")))
(magit-diff-range (string-trim version) '("--stat"))))
(defun wal-config-switch-project ()
"Switch to the config project."
(interactive)
(project-switch-project wal-default-path))
(defun wal-config-lib-files ()
"Get a list of all lib files."
(defvar wal-lib-path)
(nthcdr 2 (directory-files wal-lib-path t)))
(defun wal-config-consult-org-heading ()
"Find a heading in any of library file."
(interactive)
(consult-org-heading nil (wal-config-lib-files)))
(defun wal-customize-group ()
"Customize `wal' group."
(interactive)
(customize-group 'wal t))
(defvar wal-config--package-tag "package")
(defun wal-config-org-tags-view (&optional all-packages)
"Show tags for the config.
Calls out to `org-tags-view' while setting `org-agenda-fiels' to
the library files of the config.
Sets up `vertico' to select the prompt to allow for usage of &
and | selectors.
If ALL-PACKAGES is t, call `org-tags-view' with a pre-constructed
matcher."
(interactive "P")
(defvar wal-lib-path)
(defvar org-agenda-files)
(let ((org-agenda-files (wal-config-lib-files))
(vertico-preselect 'prompt))
(if all-packages
(org-tags-view nil wal-config--package-tag)
(call-interactively 'org-tags-view))))
(with-eval-after-load 'transient
;; Create `transient' for config package.
(transient-define-prefix whaler ()
"Facilitate the usage of or the working on Walheimat's config."
[["Find"
("f" "project" wal-config-switch-project)
("h" "heading" wal-config-consult-org-heading)
(":" "tags" wal-config-org-tags-view)]
["Act"
("t" "tangle" wal-tangle)
("x" "install expansion pack" junk-install)
("c" "customize group" wal-customize-group)
("u" "update" wal-update)]
["Help"
("m" "show diff" wal-show-config-diff-range)
("r" "show compilation result" wal-show-compilation-result)]])
(advice-add 'whaler :around 'wal-with-delayed-transient-popup))
(provide 'wal-config)
;;; wal-config.el ends here