minad / consult

:mag: consult.el - Consulting completing-read
GNU General Public License v3.0
1.12k stars 98 forks source link

Add consult--source-tab-buffer #1009

Open brsvh opened 2 weeks ago

brsvh commented 2 weeks ago

I have created a buffer source for filtering buffers within the current Tab, which should be useful for users use the tab-bar.

I browsed the issues and discussion lists and not found related discussions. Since it is a minor improvement, I directly created this PR.

If you think it is not proper, perhaps I can move it to the Wiki.

minad commented 2 weeks ago

Hi, I think it is a good addition. I made a few comments. See https://github.com/minad/consult?tab=readme-ov-file#contributions. Do you have assigned copyright to the FSF?

brsvh commented 2 weeks ago

Do you have assigned copyright to the FSF?

I have not yet, but I am willing to sign. How can I obtain the CA form? Maybe for ELPA?

minad commented 2 weeks ago

I have not yet, but I am willing to sign. How can I obtain the CA form? Maybe for ELPA?

See the CONTRIBUTE file in the Emacs source.

minad commented 2 weeks ago

I wonder about the buffer-list frame parameter. How are buffers assigned to the current tab? I haven't looked into this yet, but it seemed to me that a tab is defined by the buffers visible in it (and maybe the history of visible buffers) and there is no separation of buffers by tab.

brsvh commented 2 weeks ago

I directly use it from the source code of tab-bar--tab.

(defun tab-bar--tab (&optional frame)
  "Make a new tab data structure that can be added to tabs on the FRAME."
  (let* ((tab (tab-bar--current-tab-find nil frame))
         (tab-explicit-name (alist-get 'explicit-name tab))
         (tab-group (alist-get 'group tab))
         (bl  (seq-filter #'buffer-live-p (frame-parameter
                                           frame 'buffer-list)))
         (bbl (seq-filter #'buffer-live-p (frame-parameter
                                           frame 'buried-buffer-list))))
    (when tab-bar-select-restore-context

It seems the current-tab uses buffer-list and buried-buffer-list.

BTW, I have send the request mail to assign@gnu.org. I will inform you once I receive the copy of assignment.

minad commented 2 weeks ago

I directly use it from the source code of tab-bar--tab.

Okay, but I don't get how different tabs are different then (besides the different visible buffers). It seems to me that every buffer is part of every tab. How is a separation achieved? Can I remove buffers from a given tab or move them to another tab?

minad commented 2 weeks ago

For background - I use EXWM with tab-bar-mode to create different desktops. It would be great if I could separate the desktops more cleanly, therefore my question. The source would also be quite useful for such a use case. However as far as I've understood tabs so far, they don't really provide such a separation by design.

brsvh commented 2 weeks ago

but I don't get how different tabs are different then (besides the different visible buffers).

In fact, I did not read the code carefully either. However, I know that one can obtain all things associated with a tab through (frame-parameter nil 'tabs), and this association is constructed via tab-bar-tabs. Different tabs should only correspond to different tabs, which is not a clean method. It is not possible like virtual desktop.

Can I remove buffers from a given tab or move them to another tab?

Yes, you can move a buffer within a tab to another tab with a different index using tab-move or tab-move-to.

Kill a buffer seems to be a global action that cannot be avoided. One possible approach is to move the buffer to a staging tab instead.

minad commented 2 weeks ago

Yes, you can move a buffer within a tab to another tab with a different index using tab-move or tab-move-to.

The docstring says this:

(tab-move &optional ARG)

Move the current tab ARG positions to the right.

This operation is not about buffers as far as I understand.

brsvh commented 2 weeks ago

Whoops, I assumed too much.

I just implemented something similar that can move the current buffer to a specified tab. Maybe it can help you.

(defun tab-bar-move-current-buffer-to-tab (name)
  (interactive
   (let* ((recent-tabs (mapcar (lambda (tab)
                                 (alist-get 'name tab))
                               (tab-bar--tabs-recent))))
     (list (completing-read (format-prompt "Switch to tab by name"
                                           (car recent-tabs))
                            recent-tabs nil nil nil nil recent-tabs))))
  (let ((buffer (current-buffer))
        (buffer-list (frame-parameter nil 'buffer-list)))
    (get-buffer-window buffer)
    (select-window (get-buffer-window buffer) t)
    (if (one-window-p t)
        (bury-buffer)
      (delete-window))
    (delete buffer buffer-list)
    (tab-bar-select-tab-by-name name)
    ;; How to add `buffer' to the `buffer-list' of new tab?
    (display-buffer buffer)
    (switch-to-buffer buffer t nil)))

This is just an idea, but it indeed can work.

minad commented 2 weeks ago

Thanks, but to come back to this PR. What do you use this source for? Do you move buffers between tabs like this? How is it useful for users which don't have a such a custom move command? For example when I am in a given tab and open new buffers, they will be associated with only that tab? So this leads to some natural separation at first? However as soon as one does switch-to-buffer/consult-buffer with arbitrary buffers, the whole separation will get mixed up?

brsvh commented 2 weeks ago

My workflow is usually to manage multiple projects using Tab Bar, with many buffers open within different projects (tabs). this is usually unfriendly if I get all buffers as candidates when switching. So I use this source to filter the buffers within the current tab.

brsvh commented 2 weeks ago

For example when I am in a given tab and open new buffers, they will be associated with only that tab?

yes

minad commented 2 weeks ago

Thanks, this is very useful! I'll experiment a little bit. Please keep me posted regarding the assignment.

aikrahguzar commented 1 week ago

Thanks, but to come back to this PR. What do you use this source for? Do you move buffers between tabs like this? How is it useful for users which don't have a such a custom move command? For example when I am in a given tab and open new buffers, they will be associated with only that tab? So this leads to some natural separation at first? However as soon as one does switch-to-buffer/consult-buffer with arbitrary buffers, the whole separation will get mixed up?

In tab-bar-select-tab the buffer-list (and also buried-buffer-list) frame parameter is restored from the window configuration (using wc-bl and wc-bbl alist entries) which corresponds to the tab. I think the separation can be maintained effectively by a custom switch-to-buffer action which finds the buffer in a tab and switch to the tab before switching to buffer. switch-to-previous/next-buffer can be handled by setting a buffer-predicate frame parameter.

minad commented 1 week ago

@aikrahguzar

I think the separation can be maintained effectively by a custom switch-to-buffer action which finds the buffer in a tab and switch to the tab before switching to buffer.

Isn't the separation effective if we already restrict the displayed buffers to the ones in the frame buffer list? See https://github.com/minad/consult/commit/73971a47e12bfb54eaf72f0443a8095fa082607a.

switch-to-previous/next-buffer can be handled by setting a buffer-predicate frame parameter.

Can you please provide a code example for this?

minad commented 1 week ago

I am experimenting with the following code. Is something missing there?

(defun +tab-bar-isolated-buffer-p (buf)
  (memq buf (frame-parameter nil 'buffer-list)))
(set-frame-parameter nil 'buffer-predicate #'+tab-bar-isolated-buffer-p)

(defun +tab-bar-isolated-buffer-list ()
  (consult--buffer-query
   :sort 'visibility
   :as #'consult--buffer-pair
   :buffer-list (frame-parameter nil 'buffer-list)))

(setf (plist-get consult--source-buffer :items) #'+tab-bar-isolated-buffer-list)
aikrahguzar commented 6 days ago

Isn't the separation effective if we already restrict the displayed buffers to the ones in the frame buffer list? See 73971a4.

The scenario I was thinking was that some uses consult-buffer to select a buffer not from this source but another one. If this buffer is currently part of buffer list of some other buffer maybe it is better to switch to that tab. This is a behavior that only some people will want so probably not something to do by default.

switch-to-previous/next-buffer can be handled by setting a buffer-predicate frame parameter.

Can you please provide a code example for this?

Looking at Info node (elisp) Window History I think setting buffer-predicate is not needed. switch-to-prev/next-buffer only consider buffers in the frame's buffer list. I use tabs as barebones workspaces and set this parameter so as not switch to "temporaray buffers" but it is only needed for filtering beyond the frame's buffer list.

The code you posted above looks good except that I think +tab-bar-isolated-buffer-p is not needed.

minad commented 6 days ago

The code you posted above looks good except that I think +tab-bar-isolated-buffer-p is not needed.

This is not what I observe. I seem to need the predicate such that next-buffer and previous-buffer do not switch to other buffers.

aikrahguzar commented 6 days ago

This is not what I observe. I seem to need the predicate such that next-buffer and previous-buffer do not switch to other buffers.

You are right. (buffer-list (selected-frame)) and (frame-parameter (selected-frame) 'buffer-list) give very different results and I had assumed they were the same.