Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Non-nil uniquify-buffer-name-style breaks persp switching #104

Open
hlissner opened this issue Jun 25, 2019 · 4 comments
Open

Non-nil uniquify-buffer-name-style breaks persp switching #104

hlissner opened this issue Jun 25, 2019 · 4 comments

Comments

@hlissner
Copy link
Contributor

hlissner commented Jun 25, 2019

The problem:

If uniquify-buffer-name-style is non-nil, then buffers with conflicting names are renamed retroactively. i.e. opening x.el in one directory and x.el in another will cause uniquify to rename them to a/x.el and b/x.el. This is a problem if you've opened a/x.el in perspective 1 and b/x.el in perspective 2. Switching between the two perspectives will cause persp-mode to silently fail to load the newly activated perspective's window configuration because, presumably, the buffers were serialized with their old names and now cannot find the aforementioned buffers by their original names.

Steps to reproduce:

From vanilla Emacs (emacs -Q):

;; omitted `load-path` setup

(require 'persp-mode)
(setq uniquify-buffer-name-style 'forward
      persp-auto-save-opt 0
      persp-auto-resume-time -1)
(persp-mode +1)

(persp-switch "a")
(find-file "a/file")
(persp-add-buffer)

(persp-switch "b")
(find-file "b/file")
(persp-add-buffer)

(persp-switch "a")

Expected result:

  • In a perspective
  • Viewing a/file

Observed result:

  • In a perspective
  • Viewing b/file (perspective b's window configuration)

No messages are printed to *Messages* and no error is produced.

System information:

  • Emacs 26.2 (tested on 26.1 and 27 as well)
  • NixOS (tested on Arch and MacOS)
hlissner added a commit to doomemacs/doomemacs that referenced this issue Jun 26, 2019
- Fixes #1506: unsets uniquify-buffer-name-style to work around breakage
  in persp-mode having to do with buffers being retrospectively renamed.
  See Bad-ptr/persp-mode.el#104.
- Move creation of main workspace into persp-mode-hook, to ensure main
  workspace always exists and nil workspace is never active.
- Remove +workspaces|init-frame and significantly reduce LOC in
  +workspaces|init to the bare essentials. This _may_ break persp-mode
  in daemon Emacs, but I'll deal with that next.
uelei pushed a commit to uelei/doom-emacs that referenced this issue Aug 9, 2019
- Fixes doomemacs#1506: unsets uniquify-buffer-name-style to work around breakage
  in persp-mode having to do with buffers being retrospectively renamed.
  See Bad-ptr/persp-mode.el#104.
- Move creation of main workspace into persp-mode-hook, to ensure main
  workspace always exists and nil workspace is never active.
- Remove +workspaces|init-frame and significantly reduce LOC in
  +workspaces|init to the bare essentials. This _may_ break persp-mode
  in daemon Emacs, but I'll deal with that next.
@Bad-ptr
Copy link
Owner

Bad-ptr commented Feb 10, 2022

Something like:

(with-eval-after-load "persp-mode"
  (with-eval-after-load "uniquify"
    (defun persp--wc-bufs (wc)
      (let (buf-list)
        (cl-labels
            ((collect-wc-bufs (lst)
                              (cond
                               ((and lst (listp lst))
                                (let (head)
                                  (while (and (listp lst)
                                              (setq head (pop lst)))
                                    (cond
                                     ((listp head)
                                      (collect-wc-bufs head))
                                     (t (cl-case head
                                          ((hc vc leaf)
                                           nil)
                                          (buffer
                                           (push lst buf-list)))))))))))
          (collect-wc-bufs wc))
        buf-list))

    (defun persp-around-uniquify-rename-buffer (urb-f item buf-new-name)
      (if persp-mode
          (let* ((buf (uniquify-item-buffer item))
                 (buf-old-name (buffer-name buf))
                 (buf-persps (persp-persps))
                 (buf-new-name (funcall urb-f item buf-new-name)))
            (when (and buf-new-name (not (string= buf-old-name buf-new-name)))
              (cl-mapc
               #'(lambda (bl)
                   (when (string= (first bl) buf-old-name)
                     (setf (first bl) buf-new-name)))
               (cl-mapcan
                #'persp--wc-bufs
                (cl-mapcar #'safe-persp-window-conf buf-persps))))
            buf-new-name)
        (funcall urb-f item buf-new-name))))
  (advice-add #'uniquify-rename-buffer :around #'persp-around-uniquify-rename-buffer))

OR

(with-eval-after-load "persp-mode"
  (defun persp--wc-bufs (wc)
    (let (buf-list)
      (cl-labels
          ((collect-wc-bufs (lst)
                            (cond
                             ((and lst (listp lst))
                              (let (head)
                                (while (and (listp lst)
                                            (setq head (pop lst)))
                                  (cond
                                   ((listp head)
                                    (collect-wc-bufs head))
                                   (t (cl-case head
                                        ((hc vc leaf)
                                         nil)
                                        (buffer
                                         (push lst buf-list)))))))))))
        (collect-wc-bufs wc))
      buf-list))

  (defun persp-around-rename-buffer (rb-f &rest args)
    (if persp-mode
        (let* ((buf (current-buffer))
               (buf-old-name (buffer-name buf))
               (buf-persps ;; (persp--buffer-in-persps buf)
                (persp-persps))
               (buf-new-name (apply rb-f args)))
          ;;(message "AAA: %s %s %s" buf buf-old-name buf-new-name)
          (when (and buf-new-name (not (string= buf-old-name buf-new-name)))
            (cl-mapc
             #'(lambda (bl)
                 (when (string= (first bl) buf-old-name)
                   (setf (first bl) buf-new-name)))
             (cl-mapcan
              #'persp--wc-bufs
              (cl-mapcar #'safe-persp-window-conf buf-persps))))
          buf-new-name)
      (apply rb-f args)))
  (advice-add #'rename-buffer :around #'persp-around-rename-buffer))

@Bad-ptr
Copy link
Owner

Bad-ptr commented Feb 14, 2022

OR

(with-eval-after-load "persp-mode"
  (defun persp--wc-to-writable (wc)
    (cl-labels
        ((wc-to-writable
          (it)
          (cond
           ((and it (listp it))
            (cl-destructuring-bind (head . tail) it
              (cond
               ;; ((listp head)
               ;;  (cons (wc-to-writable head)
               ;;        (wc-to-writable tail)))
               ((eq 'parameters head)
                (let ((rw-params
                       (delq nil
                             (mapcar
                              #'(lambda (pc)
                                  (when
                                      (and
                                       (alist-get (car pc) window-persistent-parameters)
                                       (persp-elisp-object-readable-p (cdr pc)))
                                    pc))
                              tail))))
                  (if rw-params
                      `(parameters
                        ,@rw-params)
                    :delete)))
               (t
                (let ((new-head (wc-to-writable head))
                      (new-tail (wc-to-writable tail)))
                  (when (eq :delete new-tail)
                    (setq new-tail nil))
                  (if (eq :delete new-head)
                      new-tail
                    (cons new-head
                          new-tail)))))))
           ((bufferp it)
            (if (buffer-live-p it)
                (buffer-name it)
              "*Messages*"))
           ((markerp it)
            (marker-position it))
           (t it))))
      (wc-to-writable wc)))

  (setq persp-window-state-get-function
        #'(lambda (&optional frame rwin)
            (when (or rwin (setq rwin (frame-root-window
                                       (or frame (selected-frame)))))
              (window-state-get rwin nil))))

  (add-hook 'persp-before-save-state-to-file-functions
            #'(lambda (_fname phash _rpfp)
                (mapc
                 #'(lambda (persp)
                     (if persp
                         (setf (persp-window-conf persp)
                               (persp--wc-to-writable (persp-window-conf persp)))
                       (setq persp-nil-wconf
                             (persp--wc-to-writable persp-nil-wconf))))
                 (persp-persps phash)))))

updated

@awkspace
Copy link

@Bad-ptr It seems like the fix you wrote in your comment works. Have you considered adding it to the main repo?

@shackra
Copy link

shackra commented Sep 24, 2024

@Bad-ptr It seems like the fix you wrote in your comment works. Have you considered adding it to the main repo?

did this ever happen?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants