3

The command make-frame creates a duplicate frame of a given frame. Is there a reverse command which kills all duplicates (if any) of the current frame?

More precisely assume that the current frame contains a single visible window corresponding to a visiting file. I would like to delete all similar frames (i.e., all frames with the same visiting file and a single visible window), except one of them. Assume that (global-auto-revert-mode 1) is enabled, hence all visiting files have the same content.

Name
  • 7,689
  • 4
  • 38
  • 84
  • 1
    Why do you say it is a `duplicate`? The lisp object is different. `delete-frame` will delete a frame. The following snippet demonstrates that the frames are not the same: `(let ((a (make-frame)) (b (make-frame))) (message "a: %s | b: %s | eq?: %s" a b (eq a b)))` – lawlist Dec 17 '16 at 18:46
  • Note that the function `delete-frame` has two optional arguments: FRAME and FORCE. The prior must be a lisp object. Drew has a nice library to help identify/manage frames that have a specific name, and you may enjoy naming them and dealing with specially named frames to help see the difference and for organizational purposes: https://www.emacswiki.org/emacs/frame-fns.el E.g., a frame for email, a frame for org, a frame for calendar, a frame for documents, etc. You can associate buffers with frames to have frame-local buffer association with Alp Aker's `frame-bufs` library. – lawlist Dec 17 '16 at 18:53
  • Maybe I should use `clone` or something else. If I modify one of them, the other one is also modified. Please do not hesitate to suggest the correct term. – Name Dec 17 '16 at 18:58
  • You will need to establish a criteria for making the determination that two frames are sufficiently similar such that one of them should be deleted using the function `delete-frame`, and then you will need to choose which frame should be deleted -- e.g., either the frame that has focus and do not use the optional argument FRAME, or keep the selected frame that has focus and delete the other frame using the the optional argument for FRAME and pass it the appropriate lisp object as the argument. – lawlist Dec 17 '16 at 19:04
  • Here is a variation of the first snippet that specially names the frames to help demonstrate the difference between each frame: `(let ((a (make-frame '((name . "TODAY")))) (b (make-frame '((name . "TOMORROW"))))) (message "a: %s | b: %s | eq?: %s" a b (eq a b)))` The top of the frame will display the special name assigned to each frame. Each frame *might* display the same buffers, and each frame *might* display the same window layout -- however, each frame is different and can display any buffer and have any window layout -- they can even have different `default` faces and the list goes on. – lawlist Dec 17 '16 at 19:22

1 Answers1

3
(require 'cl) ;; for `copy-list'

(let* ((frame-list (frame-list))
       (current-frame (selected-frame))
       (result
         (delq nil
           (mapcar
             (lambda (frame)
               (with-selected-frame frame
                 (when (one-window-p)
                   (with-current-buffer (car (buffer-list frame))
                     (if (and buffer-file-name (file-exists-p buffer-file-name))
                       (list frame buffer-file-name))))))
           frame-list)))
       (destructable-result (copy-list result)))
  (mapc
    (lambda (x)
      (setq destructable-result (delq x destructable-result))
      (mapc
         (lambda (y)
           (when (equal (cdr x) (cdr y))
             (if (eq (car x) current-frame)
                (when (frame-live-p (car y))
                  (delete-frame (car y))
                  (setq destructable-result (delq y destructable-result)))
                (when (frame-live-p (car x))
                  (delete-frame (car x))))))
         destructable-result))
    result))

SHOW YOUR WORK: One of the concerns @lawlist had while creating the above-mentioned solution was whether altering destructable-result during an ongoing mapc loop that uses the previous value for SEQUENCE (i.e., prior to its altercation) would throw an error. The following example demonstrates that this is a non-issue.

(let ((my-list '(0 1 2 3 4 5 6 7 8 9)))
  (mapc
    (lambda (x)
      (when my-list
        (setq my-list nil))
      (message "x: %s | my-list: %s" x my-list))
    my-list))
lawlist
  • 18,826
  • 5
  • 37
  • 118