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.

  • 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, 2016 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: 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, 2016 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, 2016 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, 2016 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, 2016 at 19:22

1 Answer 1

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

(let* ((frame-list (frame-list))
       (current-frame (selected-frame))
         (delq nil
             (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))))))
       (destructable-result (copy-list result)))
    (lambda (x)
      (setq destructable-result (delq x destructable-result))
         (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))))))

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)))
    (lambda (x)
      (when my-list
        (setq my-list nil))
      (message "x: %s | my-list: %s" x my-list))
  • Thank you, I tested your code, it seems to work well, let me give a complete report later.
    – Name
    Dec 17, 2016 at 21:23
  • Currently your code, deletes the current frame and keeps one of similar frames. Is it possible to kill other similar frames and to keep the current frames?
    – Name
    Dec 17, 2016 at 21:26
  • Ready to give the second draft a try . . .
    – lawlist
    Dec 17, 2016 at 21:45
  • I confirm that your solution works well.
    – Name
    Dec 17, 2016 at 22:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.