Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add consult--source-tab-buffer #1009

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open

Conversation

brsvh
Copy link
Contributor

@brsvh brsvh commented Apr 30, 2024

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
Copy link
Owner

minad commented Apr 30, 2024

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?

consult.el Outdated Show resolved Hide resolved
consult.el Outdated
:sort 'visibility
:as #'buffer-name
:predicate #'(lambda (buf)
(memq buf (frame-parameter nil 'buffer-list))))))
Copy link
Owner

@minad minad Apr 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The predicate looks costly. Maybe let-bind the variable around the lambda? Also the #' is unnecessary.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe let-bind the variable around the lambda?

I don't understand what I should do here.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant (let ((bufs (frame-parameter ...)) (lambda (buf) (memq buf bufs))).

consult.el Outdated Show resolved Hide resolved
consult.el Show resolved Hide resolved
@brsvh
Copy link
Contributor Author

brsvh commented Apr 30, 2024

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
Copy link
Owner

minad commented Apr 30, 2024

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
Copy link
Owner

minad commented Apr 30, 2024

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
Copy link
Contributor Author

brsvh commented Apr 30, 2024

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 [email protected]. I will inform you once I receive the copy of assignment.

@minad
Copy link
Owner

minad commented Apr 30, 2024

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
Copy link
Owner

minad commented Apr 30, 2024

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
Copy link
Contributor Author

brsvh commented Apr 30, 2024

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
Copy link
Owner

minad commented Apr 30, 2024

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
Copy link
Contributor Author

brsvh commented Apr 30, 2024

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
Copy link
Owner

minad commented Apr 30, 2024

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
Copy link
Contributor Author

brsvh commented Apr 30, 2024

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
Copy link
Contributor Author

brsvh commented Apr 30, 2024

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

yes

@minad
Copy link
Owner

minad commented Apr 30, 2024

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

minad added a commit that referenced this pull request May 9, 2024
This makes it possible to restrict the buffer list to the buffers from the
current frame or current tab. See also #1009.

(setf (plist-get consult--source-buffer :items)
      (lambda ()
        (consult--buffer-query
         :sort 'visibility
         :as #'consult--buffer-pair
         :buffer-list (frame-parameter nil 'buffer-list))))
@aikrahguzar
Copy link

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
Copy link
Owner

minad commented May 10, 2024

@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 73971a4.

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
Copy link
Owner

minad commented May 10, 2024

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
Copy link

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
Copy link
Owner

minad commented May 10, 2024

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
Copy link

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.

Signed-off-by: Burgess Chang <[email protected]>

Follow the review suggestions

Signed-off-by: Burgess Chang <[email protected]>

Use :buffer-list keyword instead of predicate

Signed-off-by: Burgess Chang <[email protected]>
@brsvh
Copy link
Contributor Author

brsvh commented May 27, 2024

I have completed the signing of FSF CA and replaced the old predicate with :buffer-list, tested in my Emacs. I think we can continue here, please mention me if there is anything else that needs to be done, such as documentation.

@minad
Copy link
Owner

minad commented May 27, 2024

Thanks. I am currently testing such an isolated buffer setup right now and I am not sure if your solution provided here is sufficient. Therefore I'd like to put this PR on hold until I've figured out how to proceed, which sources we should add or if a separate consult-buffer-isolation customization variable would be the better approach.

For reference, this is the code I am testing right now (based on your work here). It is a little bit messy due to all the advices.

(defun +isolated-buffer-p (buf)
  (unless (boundp 'consult-buffer-filter)
    (require 'consult))
  (and (cl-loop with n = (buffer-name buf)
                for r in (bound-and-true-p consult-buffer-filter) never (string-match-p r n))
       (memq buf (frame-parameter nil 'buffer-list))))

(advice-add 'exwm-layout--other-buffer-predicate :before-while #'+isolated-buffer-p)

(unless (frame-parameter nil 'buffer-predicate)
  (set-frame-parameter nil 'buffer-predicate #'+isolated-buffer-p))

(+pkg consult
  (defvar +isolated-buffer-others (copy-sequence consult--source-buffer))
  (setf (plist-get +isolated-buffer-others :items)
        (lambda ()
          (consult--buffer-query
           :sort 'visibility
           :as #'consult--buffer-pair
           :buffer-list (seq-difference (buffer-list) (frame-parameter nil 'buffer-list))))
        (plist-get +isolated-buffer-others :narrow) ?t
        (plist-get +isolated-buffer-others :default) nil
        (plist-get +isolated-buffer-others :name) "Other tabs")
  (add-to-list 'consult-buffer-sources '+isolated-buffer-others t)

  (setf (plist-get consult--source-buffer :items)
        (lambda ()
          (consult--buffer-query
           :sort 'visibility
           :as #'consult--buffer-pair
           :buffer-list (frame-parameter nil 'buffer-list))))

  (defun +isolated-buffer-file-hash ()
    (consult--string-hash (consult--buffer-query
                           :as #'buffer-file-name
                           :buffer-list (frame-parameter nil 'buffer-list))))

  (advice-add #'consult--buffer-file-hash :override #'+isolated-buffer-file-hash))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants