super-save
super-save copied to clipboard
Usage of (save-excursion) SF interferes with format-on-save configuration in Clojure/Cider setups
This is technically not a bug in super-save but rather an oddity of (save-excursion), but in my Prelude setup it's coming from super-save so I am wondering if it's possible to tinker with super-save to work around it.
Expected behavior
When triggering super-save hooks with a format-on-save hook ((add-hook 'before-save-hook 'cider-format-buffer) in init.el), the file should format and the cursor should stay in its appropriate place, just like it stays there when you run cider-format-buffer or basic-buffer-save without super-save
Actual behavior
Triggering the super-save hook (e.g., navigating to a different application) saves and formats the document, but the cursor yeets to the beginning of the buffer
Steps to reproduce the problem
- Install and setup super-save as per the instructions (or by installing Prelude), and add focus-out to the trigger list
- Add (add-hook 'before-save-hook 'cider-format-buffer) to init.el to format on save
- Test by manually saving and see that the format hook runs
- Test the described behavior by navigating to a different application and returning
- The cursor should be at the beginning of the buffer
Environment & Version information
super-save version
Version: 20231209.1044
Emacs version
29.1
Operating system
macOS Sonoma 14.1.2
Adding this advice is a workaround:
(defun my-cider-format-before-save ()
"Function to run `cider-format-buffer` before `super-save-command`."
(when (and (bound-and-true-p cider-mode)
(derived-mode-p 'clojure-mode 'clojurescript-mode 'clojurec-mode))
(cider-format-buffer)))
(advice-add 'super-save-command :before #'my-cider-format-before-save)
I added this to init.el, not sure if that's the correct location but it worked there.
Hmm, that's weird. The command that super-save
runs to save a buffer is quite simple:
(defun super-save-buffer (buffer)
"Save BUFFER if needed, super-save style."
(with-current-buffer buffer
(save-excursion
(when (super-save-p)
(super-save-delete-trailing-whitespace-maybe)
(if super-save-silent
(with-temp-message ""
(let ((inhibit-message t)
(inhibit-redisplay t)
(message-log-max nil))
(basic-save-buffer)))
(basic-save-buffer))))))
That's why it's not immediately clear what causes this weird interaction that you've experienced.
Thank you for commenting so fast.
The strange interaction is actually between cider-format-buffer and check-excursion. You can test it by creating an improperly formatted clojure file and running the evaluate expression function on (check-excursion (cider-format-buffer)). Note that running format without check-excursion doesn't yeet the cursor.
I discovered this after setting up a simple pre-save hook to cider-format-buffer my clojure files, then noticed when I click away from Emacs my cursor jumps to the top of the buffer. I am quite new to Emacs having installed it for the first time two days ago, and after sleuthing around found that the interaction between (check-excursion) and (cider-format-buffer) has to do with super-save and my pre-save hook.
Adding an advice before super-save-command fixes that interaction, though, because now cider-format-buffer runs outside of the check-excursion form. You can even keep the pre-save hook in this case for manual saves which I like to do, because now since there's a pre-super-save hook, running format inside check-excursion doesn't do anything (you're running format twice), and since the buffer doesn't change, check-excursion doesn't get confused about where to place the cursor.