Álvaro Ramírez
Deleting from Emacs sequence vars
Adding hooks and setting variables is core to customizing Emacs. Take a major mode like
emacs-lisp-mode
as an example. To customize its behaviour, one may add a hook function to emacs-lisp-mode-hook
, or if you're a little lazy while experimenting, you may even use a lambda.
(add-hook 'emacs-lisp-mode-hook #'my/emacs-lisp-mode-config) (add-hook 'emacs-lisp-mode-hook (lambda () (message "I woz ere")))
emacs-lisp-mode-hook
's content would subsequently look as follows:
'(my/emacs-lisp-mode-config (lambda nil (message "I woz ere")) ert--activate-font-lock-keywords easy-escape-minor-mode lisp-extra-font-lock-global-mode)
Maybe my/emacs-lisp-mode-config
didn't work out for us and we'd like to remove it. We can use remove-hook
for that and evaluate something like:
(remove-hook 'emacs-lisp-mode-hook #'my/emacs-lisp-mode-config)
The lambda can be removed too, but you ought to be careful in using the same lambda body.
(remove-hook 'emacs-lisp-mode-hook (lambda () (message "I woz tere")))
There are other ways to remove the lambdas, but we're digressing here… We typically have to write these throwaway snippets to undo our experiments. What if we just had a handy helper always available to remove items from sequences (edit: we do, remove-hook
is already interactive, see Update 2 below)? After all, hooks are just lists (sequences).
While the interactive command can likely be simplified further, I tried to optimize for ergonomic usage. For example, completing-read
gives us a way narrow down whichever variable we'd like to modify as well as the item we'd like to remove. seqp
is also handy, as we filter out noise by automatically removing any variable that's not a sequence.
(defun ar/remove-from-list-variable () (interactive) (let* ((var (intern (completing-read "From variable: " (let (symbols) (mapatoms (lambda (sym) (when (and (boundp sym) (seqp (symbol-value sym))) (push sym symbols)))) symbols) nil t))) (values (mapcar (lambda (item) (setq item (prin1-to-string item)) (concat (truncate-string-to-width (nth 0 (split-string item "\n")) (window-body-width)) (propertize item 'invisible t))) (symbol-value var))) (index (progn (when (seq-empty-p values) (error "Already empty")) (seq-position values (completing-read "Delete: " values nil t))))) (unless index (error "Eeek. Something's up.")) (set var (append (seq-take (symbol-value var) index) (seq-drop (symbol-value var) (1+ index)))) (message "Deleted: %s" (truncate-string-to-width (seq-elt values index) (- (window-body-width) 9)))))
Hooks are just an example of lists we can delete from. I recently used the same command on display-buffer-alist
.
While this has been a fun exercise, I can't help but think that I'm likely re-inventing the wheel here. Is there something already built-in that I'm missing?
Update 1
alphapapa suggested some generalizations that would provide an editing buffer of sorts. This is a neat idea, using familiar key bindigs C-c C-c
to save and C-c C-k
to bail.
Beware, I haven't tested the code with a diverse set of list items, so there's a chance of corrupting the variable content. Improvements to the code are totally welcome.
;;; -*- lexical-binding: t; -*- (defun ar/edit-list-variable () (interactive) (let* ((var (intern (completing-read "From variable: " (let (symbols) (mapatoms (lambda (sym) (when (and (boundp sym) (seqp (symbol-value sym))) (push sym symbols)))) symbols) nil t))) (values (string-join (mapcar #'prin1-to-string (symbol-value var)) "\n"))) (with-current-buffer (get-buffer-create "*eval elisp*") (emacs-lisp-mode) (local-set-key (kbd "C-c C-c") (lambda () (interactive) (eval-buffer) (kill-this-buffer) (message "Saved: %s" var))) (local-set-key (kbd "C-c C-k") 'kill-this-buffer) (erase-buffer) (insert (format "(setq %s\n `(%s))" var values)) (mark-whole-buffer) (indent-region (point-min) (point-max)) (deactivate-mark) (switch-to-buffer (current-buffer)))))
Update 2
So hunch was right…
"While this has been a fun exercise, I can't help but think that I'm likely re-inventing the wheel here. Is there something already built-in that I'm missing?"
juicecelery's Reddit commit confirmed it. Thank you! remove-hook is already interactive 🤦♂️. TIL 😁
juicecelery was kind enough to point out an improvement in the custom function:
"but I see your improvements, for instance that non list items are removed from the selection."