3

How can I open magit-status in a new frame? I've tried to write a custom function for this but it has some issues as described below.

  (spacemacs/set-leader-keys "gs"
    (lambda () (interactive)
      (when (not (string-prefix-p "magit:" (substring-no-properties (cdr (assoc 'name (frame-parameters))))))
        (select-frame (make-frame-command))
        )
      (magit-status)
      )
    )

I have the above code, however this opens 'multiple' frames if the above is executed multiple times. Is there a way I can prevent it from opening a new frame if an existing magit frame already exists?

Is there possibly an alternative? Otherwise I'd imagine I'd need to get a list of all frames, search for the magit frame and select it.

2
  • 1
    I would imagine that Tarsius may already have something that does what you want. If he does not, the following link contains a complex example of how to target a specific frame (and window within that frame) with a non-file-visiting-buffer and/or a file-visiting-buffer. The best way to use that example is to have a function that creates/populates the target buffer without displaying it anywhere, and then use the display-buffer-alist and a custom function like in the linked example. stackoverflow.com/questions/18346785/…
    – lawlist
    Commented Jul 17, 2018 at 1:17
  • I don’t have a full example, but I’d think customizing magit-display-buffer-function might be the way to go? magit.vc/manual/magit/Switching-Buffers.html#Switching-Buffers
    – Michael
    Commented Jul 17, 2018 at 6:26

2 Answers 2

4

Try using the following function as magit-display-buffer-function:

;; (require 'magit)

(defun magit-display-buffer-pop-up-frame (buffer)
  (if (with-current-buffer buffer (eq major-mode 'magit-status-mode))
      (display-buffer buffer
                      '((display-buffer-reuse-window
                         display-buffer-pop-up-frame)
                        (reusable-frames . t)))
    (magit-display-buffer-traditional buffer)))

(setq magit-display-buffer-function #'magit-display-buffer-pop-up-frame)

This says, "If the to-be-displayed buffer is a Magit status buffer, first try to show a window in any frame that is already displaying that buffer. If that fails, display the buffer in a new frame. For non-status buffers, just use Magit's default behavior."

2
  • 1
    Do note two things: 1) if you use non-GUI emacs at all, this solution results in magit-status replacing the current buffer, which may or may not be what you want; and 2) that this solution won't work well if you use emacsclient from different endpoints, as it will re-use an available status window even if it's on a different display (or in a GUI when you're using SSH). I suggested other-frame-window because it handles this case correctly, as well as letting you explicitly choose.
    – Trey
    Commented Jul 18, 2018 at 21:39
  • This works for within emacs however, if trying to open via command line, it still seems to open multiple frames. For example via emacsclient -c --eval '(magit-status)'. Commented Jun 24, 2020 at 8:48
2

I'm not familiar with Spacemacs, so hopefully you can apply this answer and let me know what modifications are necessary, and I can edit this answer so that it's applicable to both Spacemacs and other Emacs users.

The easiest way to achieve this in Emacs is to load the package other-frame-window (it's available using list-packages or package-install, which from reading docs seem to be readily available in Spacemacs).

In traditional Emacs, the C-x 4 and C-x 5 prefixes have been used to operate on windows and frames, respectively, but they're not easily extensible and they operate in a slightly unintuitive way, so other-frame-window attempts to address this by creating two new prefixes at C-x 7 and C-x 9, both of which have the same following map, but the C-x 7 commands operate on an (Emacs) window, and C-x 9 on frames.

While it supports some built-in commands (which you can read about in the library's docs) for deleting windows/frames, moving them about, etc., the default thing it does when presented with an interactive command is to create a new window or frame and run the command in that new context. So, doing what you want is as simple as applying the following Elisp in your init file:

(other-frame-window-mode)
(define-key ofw-transient-map (kbd "z") #'magit-status)

Here, we first enable the OFW minor mode. (Since other-frame-window operates as a minor mode, you'll need to enable it — to start, simply executing other-frame-window-mode in the buffer you want to use will let you try it. You can run the command again to disable it.)

Then, we add z to the keyboard map to invoke magit-status. Once you do this, C-x 9 z will open Magit in another frame.

(C-x 7 z, will, obviously, open Magit in a new window, but that's relatively useless since that's the default behavior. This default behavior, by the way, is controlled by magit-display-buffer-function as was mentioned in Michael's comment to your question, but all the standard choices work on windows, not frames, and rigging up your own with this variable as suggested is rather difficult, and exactly the plumbing that other-frame-window takes care of for you.)

You could, of course, define a macro to do this directly without needing three keystrokes, or if you dip into the source code in other-frame-window.el, you can roll your own interactive function to do it directly.

Note that Magit will not dismiss upon pressing q in the case when it's the only buffer in the frame. That's because it's well-behaved and does a bury-buffer rather than kill-buffer, so that if the current frame happens to be the only frame, you won't accidentally kill Emacs. You could override this, but I'd advise against it. You can just kill the frame as usual, or use a prefix argument to q (in regular Emacs, that's C-u q) to force it to kill the buffer, frame and all.

One final remark: note that, in general, it's not a great idea to hand-write functions that unilaterally open new frames because there are many reasons why Emacs may be unable to do so — most notably, because you're operating in a non-GUI environment like a terminal emulator, SSH or Mosh session. Correctly-written frame-handling commands deal with this appropriately, so deferring this logic to other-frame-window seems like a good way to get the functionality you want without reinventing the wheel.

1
  • For some reason the code "(define-key ofw-transient-map (kbd "z") #'magit-status)" does not work on my end. It hangs. Commented Aug 11, 2020 at 3:45

Your Answer

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

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