0
votes

I have been using Emacs (at the moment 26.3) for quite a while (years). I always had

(use-package saveplace
  :config
  (save-place-mode t))

in my init.el, which makes Emacs use save-place-mode (which comes with vanilla Emacs as saveplace.el) in order to remember the last position in file.

My Problem with saveplace is, that it keeps saving visited files and positions to a list called save-place-alist and only through a hook in kill-emacs-hook it dumps this list to default location ~/.emacs.d/places. But it occurs quite often, that i forget or somehow not normally quit my emacs-daemon. So, the list will be lost.

My approach was to extract the function save-place-alist-to-file from saveplace.el and add it to the kill-buffer-hook so now, everytime i kill a buffer save-place-alist would be saved to disk. This is what my save-place config looks now:

(defun my-save-place-alist-to-file ()
(interactive)
(save-place-to-alist)
  (let ((file (expand-file-name save-place-file))
        (coding-system-for-write 'utf-8))
    (with-current-buffer (get-buffer-create " *Saved Places*")
      (delete-region (point-min) (point-max))
      (when save-place-forget-unreadable-files
        (save-place-forget-unreadable-files))
      (insert (format ";;; -*- coding: %s -*-\n"
                      (symbol-name coding-system-for-write)))
      (let ((print-length nil)
            (print-level nil))
        (pp save-place-alist (current-buffer)))
      (let ((version-control
             (cond
              ((null save-place-version-control) nil)
              ((eq 'never save-place-version-control) 'never)
              ((eq 'nospecial save-place-version-control) version-control)
              (t
               t))))
        (condition-case nil
            ;; Don't use write-file; we don't want this buffer to visit it.
            (write-region (point-min) (point-max) file)
          (file-error (message "Saving places: can't write %s" file)))
        (kill-buffer (current-buffer))))))

(use-package saveplace
  :config
  (save-place-mode t)
  ;; (remove-hook 'kill-buffer-hook 'save-place-to-alist)
  ;; (add-hook 'kill-buffer-hook 'my-save-place-alist-to-file))

Everything i fine, as long as i dont modify the kill-buffer-hook by uncommenting the last to lines. The function my-save-place-alist-to-file works properly when called interactively. But when activate the hooks, literally everything brakes. First of all i can not kill buffers anymore. I get this error related to recursion depth:

progn: Variable binding depth exceeds max-specpdl-size

The debugger says:

Debugger entered--Lisp error: (error "Variable binding depth exceeds max-specpdl-size")
  generate-new-buffer(" *temp*")
  pp-to-string((("/home/.../test.txt" . 33)))
  pp((("/home/.../test.txt" . 33)) #<buffer  *Saved Places*>)
  (let ((print-length nil) (print-level nil)) (pp save-place-alist (current-buffer)))
  (save-current-buffer (set-buffer (get-buffer-create " *Saved Places*")) (delete-region (point-min) (point-max)) (if save-place-forget-unreadable-files (progn (save-place-forget-unreadable-files))) (insert (format ";;; -*- coding: %s -*-\n" (symbol-name coding-system-for-write))) (let ((print-length nil) (print-level nil)) (pp save-place-alist (current-buffer))) (let ((version-control (cond ((null save-place-version-control) nil) ((eq (quote never) save-place-version-control) (quote never)) ((eq (quote nospecial) save-place-version-control) version-control) (t t)))) (condition-case nil (write-region (point-min) (point-max) file) (file-error (message "Saving places: can't write %s" file))) (kill-buffer (current-buffer))))
  (let ((file (expand-file-name save-place-file)) (coding-system-for-write (quote utf-8))) (save-current-buffer (set-buffer (get-buffer-create " *Saved Places*")) (delete-region (point-min) (point-max)) (if save-place-forget-unreadable-files (progn (save-place-forget-unreadable-files))) (insert (format ";;; -*- coding: %s -*-\n" (symbol-name coding-system-for-write))) (let ((print-length nil) (print-level nil)) (pp save-place-alist (current-buffer))) (let ((version-control (cond ((null save-place-version-control) nil) ((eq ... save-place-version-control) (quote never)) ((eq ... save-place-version-control) version-control) (t t)))) (condition-case nil (write-region (point-min) (point-max) file) (file-error (message "Saving places: can't write %s" file))) (kill-buffer (current-buffer)))))
  my-save-place-alist-to-file()
  kill-buffer(#<buffer  *temp*-840370>)
  #f(compiled-function () #<bytecode 0x1c65c75>)()
  pp-to-string((("/home/.../test.txt" . 33)))
  pp((("/home/.../test.txt" . 33)) #<buffer  *Saved Places*>)
  (let ((print-length nil) (print-level nil)) (pp save-place-alist (current-buffer)))
  (save-current-buffer (set-buffer (get-buffer-create " *Saved Places*")) (delete-region (point-min) (point-max)) (if save-place-forget-unreadable-files (progn (save-place-forget-unreadable-files))) (insert (format ";;; -*- coding: %s -*-\n" (symbol-name coding-system-for-write))) (let ((print-length nil) (print-level nil)) (pp save-place-alist (current-buffer))) (let ((version-control (cond ((null save-place-version-control) nil) ((eq (quote never) save-place-version-control) (quote never)) ((eq (quote nospecial) save-place-version-control) version-control) (t t)))) (condition-case nil (write-region (point-min) (point-max) file) (file-error (message "Saving places: can't write %s" file))) (kill-buffer (current-buffer))))
  (let ((file (expand-file-name save-place-file)) (coding-system-for-write (quote utf-8))) (save-current-buffer (set-buffer (get-buffer-create " *Saved Places*")) (delete-region (point-min) (point-max)) (if save-place-forget-unreadable-files (progn (save-place-forget-unreadable-files))) (insert (format ";;; -*- coding: %s -*-\n" (symbol-name coding-system-for-write))) (let ((print-length nil) (print-level nil)) (pp save-place-alist (current-buffer))) (let ((version-control (cond ((null save-place-version-control) nil) ((eq ... save-place-version-control) (quote never)) ((eq ... save-place-version-control) version-control) (t t)))) (condition-case nil (write-region (point-min) (point-max) file) (file-error (message "Saving places: can't write %s" file))) (kill-buffer (current-buffer)))))
  my-save-place-alist-to-file()
  kill-buffer(#<buffer  *temp*-437350>)
  #f(compiled-function () #<bytecode 0x1c65c35>)()
  pp-to-string((("/home/.../test.txt" . 33)))
  pp((("/home/.../test.txt" . 33)) #<buffer  *Saved Places*>)
  (let ((print-length nil) (print-level nil)) (pp save-place-alist (current-buffer)))
  (save-current-buffer (set-buffer (get-buffer-create " *Saved Places*")) (delete-region (point-min) (point-max)) (if save-place-forget-unreadable-files (progn (save-place-forget-unreadable-files))) (insert (format ";;; -*- coding: %s -*-\n" (symbol-name coding-system-for-write))) (let ((print-length nil) (print-level nil)) (pp save-place-alist (current-buffer))) (let ((version-control (cond ((null save-place-version-control) nil) ((eq (quote never) save-place-version-control) (quote never)) ((eq (quote nospecial) save-place-version-control) version-control) (t t)))) (condition-case nil (write-region (point-min) (point-max) file) (file-error (message "Saving places: can't write %s" file))) (kill-buffer (current-buffer))))
  (let ((file (expand-file-name save-place-file)) (coding-system-for-write (quote utf-8))) (save-current-buffer (set-buffer (get-buffer-create " *Saved Places*")) (delete-region (point-min) (point-max)) (if save-place-forget-unreadable-files (progn (save-place-forget-unreadable-files))) (insert (format ";;; -*- coding: %s -*-\n" (symbol-name coding-system-for-write))) (let ((print-length nil) (print-level nil)) (pp save-place-alist (current-buffer))) (let ((version-control (cond ((null save-place-version-control) nil) ((eq ... save-place-version-control) (quote never)) ((eq ... save-place-version-control) version-control) (t t)))) (condition-case nil (write-region (point-min) (point-max) file) (file-error (message "Saving places: can't write %s" file))) (kill-buffer (current-buffer)))))
  my-save-place-alist-to-file()
...

I already did try a lot things to fix this. My first guess was, that the final call of (kill-buffer (current-buffer)) would lead to the infinite recursion. But removing it does not help.

To summarize, my question is:
* Why and where does the infinite recursion happen?

1
It seems the code creates a temp buffer which when killed invokes the function again in its kill-buffer-hook. I guess the temp buffer is created to save the places. Maybe disable the hook inside the function? - choroba
@Drew alright, edited the question. - friedemann

1 Answers

0
votes

I tried what @choroba mentioned by adding a

(let 'kill-buffer-hook nil
...
) 

around the whole part after (save-place-to-alist) which indeed removed the infinite recursion problem! But then another problem emerged: this hooked got called everytime i switched buffers, which-key popped up and down and in many more occasions. Everytime writing to ~/.emacs.d/places. So this was not what i wanted.

Meanwhile I've found a convenient solution: adding the function not to the kill-buffer-hook but to after-save-hook. Which is sufficient for me :)

My final save-place-setup which works fine for me now looks like this:

(defun my-save-place-alist-to-file ()
  (interactive)
  (save-place-to-alist)
  (let ((file (expand-file-name save-place-file))
        (coding-system-for-write 'utf-8))
    (with-current-buffer (get-buffer-create " *Saved Places*")
      (delete-region (point-min) (point-max))
      (when save-place-forget-unreadable-files
        (save-place-forget-unreadable-files))
      (insert (format ";;; -*- coding: %s -*-\n"
                      (symbol-name coding-system-for-write)))
      (let ((print-length nil)
            (print-level nil))
        (pp save-place-alist (current-buffer)))
      (let ((version-control
             (cond
              ((null save-place-version-control) nil)
              ((eq 'never save-place-version-control) 'never)
              ((eq 'nospecial save-place-version-control) version-control)
              (t
               t))))
        (condition-case nil
            ;; Don't use write-file; we don't want this buffer to visit it.
            (write-region (point-min) (point-max) file nil) ; 4th arg nil means: do not append!
          (file-error (message "Saving places: can't write %s" file))
          (kill-buffer (current-buffer)))))))

(use-package saveplace
  :config
  (save-place-mode t)
  (add-hook 'after-save-hook 'my-save-place-alist-to-file)) ; after-save-hook seems to be the best place for this