bmag / emacs-purpose

Manage Windows and Buffers According to Purposes
GNU General Public License v3.0
496 stars 23 forks source link

Popup window not killed if window reused for other buffer #153

Closed Zulu-Inuoe closed 3 years ago

Zulu-Inuoe commented 5 years ago

When we reuse a popup window to display some other popup buffer, and then we quit from that buffer via (quit-window t) or the like, we will be back at our previous popup buffer, and have our quit-restore parameter set to nil.

Unfortunately then, quit-window once more will cause the window to display a random buffer because when quit-restore is nil, quit-window will not actually kill the window because ????

This means that our popup window will now be displaying a random buffer, rather than having been killed as it should have been.

bmag commented 5 years ago

The usage of purpose-change-buffer and window--display-buffer throughout Purpose is supposed to play correctly with quit-window. IIRC this happens also without Purpose, so I didn't investigate too closely after my initial research. Didn't evaluate the PR yet, but is the problem really only with popwin extension?

Zulu-Inuoe commented 5 years ago

This is a bit of a complicated issue, and I was skeptical with the 'fix' so let me give a full reproduction so maybe you can verify and sanity check me on this:

  1. Set up purpose:
    (purpose-mode)
    (require 'window-purpose-x)
    (purpose-x-popwin-setup)
  2. Add an additional testing config to get *Messages* to be have popup purpose:
    (purpose-set-extension-configuration :testing-popup (purpose-conf :name-purposes '(("*Messages*" . popup))))
  3. Switch to the *Messages* buffer. Should get a popup window.
  4. Press C-h k C-n to open up a help buffer. Should reuse the same window.
  5. Press q to quit the help window. This takes us back to the *Messages* buffer in the same window.
  6. Press q to quit the *Messages* buffer.

Now instead of the popup window having been killed, we have some other random buffer being displayed in that window.

I tracked down this behaviour to the specific rules in which Emacs will actually kill a window in response to quit-window: https://www.gnu.org/software/emacs/manual/html_node/elisp/Quitting-Windows.html

Specifically (I formatted it a bit to make it more readable..):

The window is deleted entirely if:

  1. the first element of the quit-restore parameter is one of 'window or 'frame
  2. the window has no history of previously-displayed buffers and
  3. the displayed buffer matches the one in the fourth element of the quit-restore parameter.

When the popup window is first created, it indeed has the right quit-restore-parameter. But after that quit-window call from step 5, it is set to nil because..:

The function's behavior is determined by the four elements of the list specified by the quit-restore window parameter (see Window Parameters), which is set to nil afterwards.

I couldn't think of a way to play nice with quit-window with this behaviour, so the PR is indeed a bit of a hacky workaround. I'd love to have a cleaner way to go about it, if somebody can come up with it. Maybe I overlooked something.

Sorry for the length post!