super-save icon indicating copy to clipboard operation
super-save copied to clipboard

Usage of (save-excursion) SF interferes with format-on-save configuration in Clojure/Cider setups

Open nsadeh opened this issue 1 year ago • 3 comments

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

  1. Install and setup super-save as per the instructions (or by installing Prelude), and add focus-out to the trigger list
  2. Add (add-hook 'before-save-hook 'cider-format-buffer) to init.el to format on save
  3. Test by manually saving and see that the format hook runs
  4. Test the described behavior by navigating to a different application and returning
  5. 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

nsadeh avatar Dec 12 '23 18:12 nsadeh

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.

nsadeh avatar Dec 12 '23 18:12 nsadeh

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.

bbatsov avatar Dec 12 '23 20:12 bbatsov

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.

nsadeh avatar Dec 12 '23 20:12 nsadeh