bug#38354: 27.0.50; Implement display action display-buffer-in-tab

classic Classic list List threaded Threaded
19 messages Options
Reply | Threaded
Open this post in threaded view
|

bug#38354: 27.0.50; Implement display action display-buffer-in-tab

Juri Linkov-2
Severity: wishlist

Like demonstrated in bug#37998 we need a new display action
display-buffer-in-tab to use in display-buffer-alist.  Examples:

  ("\\`\\*Group\\*\\'" .
   (display-buffer-in-tab
    (name . "Gnus")))

  ("\\`\\*Summary \\(.*\\)\\*\\'" .
   (display-buffer-in-tab
    (name . "Group \\1")))

The only problem is that I don't know how to use matched numbered groups
\1 from matches of buffer names in display conditions.

display-buffer-assq-regexp uses string-match-p, not string-match.



Reply | Threaded
Open this post in threaded view
|

bug#38354: 27.0.50; Implement display action display-buffer-in-tab

martin rudalics
 > The only problem is that I don't know how to use matched numbered groups
 > \1 from matches of buffer names in display conditions.
 >
 > display-buffer-assq-regexp uses string-match-p, not string-match.

If you really need 'string-match', we have to save the match data.

martin



Reply | Threaded
Open this post in threaded view
|

bug#38354: 27.0.50; Implement display action display-buffer-in-tab

Juri Linkov-2
>> The only problem is that I don't know how to use matched numbered groups
>> \1 from matches of buffer names in display conditions.
>>
>> display-buffer-assq-regexp uses string-match-p, not string-match.
>
> If you really need 'string-match', we have to save the match data.

This is implemented now by using a lambda for tab name (the last example).
Examples:

1. open the buffer test1 in a new tab

  (push '("\\`test1\\'" .
          (display-buffer-in-tab))
        display-buffer-alist)

2. open the buffer test2 in a new tab with the explicit name "Tab test2"

  (push '("\\`test2\\'" .
          (display-buffer-in-tab
           (name . "Tab test2")))
        display-buffer-alist)

3. open the buffer test3 in a new tab with the explicit name constructed
   from the buffer name:

  (push '("\\`test3\\'" .
          (display-buffer-in-tab
           (name . (lambda (buffer _alist)
                     (format "[%s]" buffer)))))
        display-buffer-alist)



Reply | Threaded
Open this post in threaded view
|

bug#38354: 27.0.50; Implement display action display-buffer-in-tab

Juri Linkov-2
In reply to this post by martin rudalics
>> The only problem is that I don't know how to use matched numbered groups
>> \1 from matches of buffer names in display conditions.
>>
>> display-buffer-assq-regexp uses string-match-p, not string-match.
>
> If you really need 'string-match', we have to save the match data.

display-buffer-in-tab is implemented now, but we need also an action
to display the buffer in an existing tab if such buffer is
already displayed in it.  I tried to copy an existing action
that supports frames, but can't find such a frame action that
would select another frame if the buffer is already is displayed in it.
Does such frame action exist whose behavior could be copied to tabs?

This will require a new function function tab-bar-buffer-visible-in-tabs.

Also I use this function in a wrapper that kills the buffer, such wrapper checks

  (tab-bar-buffer-visible-in-tabs-p (current-buffer))

If true, it doesn't kill the buffer, but buries it.
So switching back to that tab still displays the buffer.

Another place where I use tab-bar-buffer-visible-in-tabs-p
is to save Dired buffers to the desktop file only
when the Dired buffer is displayed in a tab:

  (setq desktop-buffers-not-to-save-function
        (lambda (_filename bufname mode &rest _)
          (or (not (memq mode '(dired-mode vc-dir-mode)))
              (memq (get-buffer bufname) (mapcar #'window-buffer (window-list-1)))
              (tab-bar-buffer-visible-in-tabs-p (get-buffer bufname)))))

This also required changes in desktop.el to support new predicate function
desktop-buffers-not-to-save-function.


diff --git a/lisp/window.el b/lisp/window.el
index c750ea71ea..b46abd6ced 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -6231,6 +6231,17 @@ window-state-put
     (delete-window window))))
       (window--check frame))))
 
+(defun window-state-buffers (state)
+  "Return all buffers saved to the given window state STATE."
+  (let ((buffer (cadr (assq 'buffer state)))
+        (buffers (mapcan (lambda (item)
+                           (when (memq (car item) '(leaf vc hc))
+                             (window-state-buffers item)))
+                         (if (consp (car state)) (list (cdr state)) (cdr state)))))
+    (if buffer
+        (cons (get-buffer buffer) buffers)
+      buffers)))
+
 (defun window-swap-states (&optional window-1 window-2 size)
   "Swap the states of live windows WINDOW-1 and WINDOW-2.
 WINDOW-1 must specify a live window and defaults to the selected
diff --git a/lisp/tab-bar.el b/lisp/tab-bar.el
index 5eb332884c..9c8c38fb83 100644
--- a/lisp/tab-bar.el
+++ b/lisp/tab-bar.el
@@ -1284,6 +1284,12 @@ display-buffer-in-tab
               (tab-bar-rename-tab name))))
       (tab-bar-new-tab))))
 
+(defun tab-bar-buffer-visible-in-tabs-p (buffer)
+  "Return non-nil when BUFFER is visible in other tabs."
+  (seq-some (lambda (tab)
+              (memq buffer (window-state-buffers (cdr (assq 'ws tab)))))
+            (funcall tab-bar-tabs-function)))
+
 
 (defun switch-to-buffer-other-tab (buffer-or-name &optional norecord)
   "Switch to buffer BUFFER-OR-NAME in another tab.
diff --git a/lisp/desktop.el b/lisp/desktop.el
index 498f769bd3..6f45278218 100644
--- a/lisp/desktop.el
+++ b/lisp/desktop.el
@@ -946,7 +946,9 @@ desktop-outvar
       ")\n"))))
 
 ;; ----------------------------------------------------------------------------
-(defun desktop-save-buffer-p (filename bufname mode &rest _dummy)
+(defvar desktop-buffers-not-to-save-function nil)
+
+(defun desktop-save-buffer-p (filename bufname mode &rest rest)
   "Return t if buffer should have its state saved in the desktop file.
 FILENAME is the visited file name, BUFNAME is the buffer name, and
 MODE is the major mode.
@@ -970,6 +972,9 @@ desktop-save-buffer-p
      (and (null filename)
   (null dired-skip)  ; bug#5755
   (with-current-buffer bufname desktop-save-buffer)))
+ (or (null desktop-buffers-not-to-save-function)
+     (funcall desktop-buffers-not-to-save-function
+      filename bufname mode rest))
  t)))
 
 ;; ----------------------------------------------------------------------------
Reply | Threaded
Open this post in threaded view
|

bug#38354: 27.0.50; Implement display action display-buffer-in-tab

martin rudalics
 > display-buffer-in-tab is implemented now, but we need also an action
 > to display the buffer in an existing tab if such buffer is
 > already displayed in it.

Could we please clarify the term "display(ed)" in this context.  IIUC
you use it

- to say that a buffer is part of a tab (a window configuration that
   can be shown on a frame), and

- to say that a buffer is actually displayed on a frame that has a
   tab-bar.

Right?  Then the former should not use the term "display(ed)" but
maybe something like "tab(bed)".  "Tab a buffer" would then mean to
make sure that the buffer is part of a tab, "tabbed" that it is part
of at least one tab.

 > I tried to copy an existing action
 > that supports frames, but can't find such a frame action that
 > would select another frame if the buffer is already is displayed in it.
 > Does such frame action exist whose behavior could be copied to tabs?

'display-buffer-reuse-window' together with 'reusable-frames' should
have all the ingredients for this.  What is missing?

 > This will require a new function function tab-bar-buffer-visible-in-tabs.

What would "visible" precisely stand for here?  And why "tabs"
indiscriminately?  Don't you ever want to check for presence or
visibility in a specific tab only?

 > Also I use this function in a wrapper that kills the buffer, such wrapper checks
 >
 >    (tab-bar-buffer-visible-in-tabs-p (current-buffer))
 >
 > If true, it doesn't kill the buffer, but buries it.

Via 'kill-buffer-query-functions'?

 > So switching back to that tab still displays the buffer.

When I do 'kill-buffer', I expect that buffer to get removed from
'buffer-list' and all windows showing it, that it won't be switched to
by many functions and so on.  Whatever we'd do, we have to manage this
controversy somehow.  Think of changes or the deletion of the visited
files.  How would we try to avoid saving such buffers to their files
in that case?

martin



Reply | Threaded
Open this post in threaded view
|

bug#38354: 27.0.50; Implement display action display-buffer-in-tab

Juri Linkov-2
>> display-buffer-in-tab is implemented now, but we need also an action
>> to display the buffer in an existing tab if such buffer is
>> already displayed in it.
>
> Could we please clarify the term "display(ed)" in this context.  IIUC
> you use it
>
> - to say that a buffer is part of a tab (a window configuration that
>   can be shown on a frame), and

In my code I use for this:

  (tab-bar-buffer-visible-in-tabs-p buffer)

> - to say that a buffer is actually displayed on a frame that has a
>   tab-bar.

In my code I use for this:

  (>= (length (get-buffer-window-list buffer t t)) 1)

i.e. I check these situations differently, and use 'or'
to combine these conditions:

  (or (>= (length (get-buffer-window-list buffer t t)) 1)
      (tab-bar-buffer-visible-in-tabs-p buffer))

Should these conditions be combined in one function
(if the current tab can be considered a tab as well)?

> Right?  Then the former should not use the term "display(ed)" but
> maybe something like "tab(bed)".  "Tab a buffer" would then mean to
> make sure that the buffer is part of a tab, "tabbed" that it is part
> of at least one tab.

Not sure if "tabbed" is the right word.  None of these definitions fits:
https://www.dictionary.com/browse/tabbed
https://www.urbandictionary.com/define.php?term=tabbed

>> This will require a new function function tab-bar-buffer-visible-in-tabs.
>
> What would "visible" precisely stand for here?

Maybe a better word is "has"?  Then the name would be tab-bar-has-buffer-in-tab.

> And why "tabs" indiscriminately?  Don't you ever want to check for
> presence or visibility in a specific tab only?

A specific tab referred by name?  Maybe such function could be useful as well.
But currently there is need for a function that returns a tab that has
the given buffer:

diff --git a/lisp/tab-bar.el b/lisp/tab-bar.el
index 5eb332884c..7746c59f17 100644
--- a/lisp/tab-bar.el
+++ b/lisp/tab-bar.el
@@ -1284,6 +1284,13 @@ display-buffer-in-tab
+(defun tab-bar-has-buffer-in-tab (buffer)
+  "Return a tab that has the buffer BUFFER."
+  (seq-some (lambda (tab)
+              (when (memq buffer (window-state-buffers (cdr (assq 'ws tab))))
+                tab))
+            (funcall tab-bar-tabs-function)))

>> I tried to copy an existing action
>> that supports frames, but can't find such a frame action that
>> would select another frame if the buffer is already is displayed in it.
>> Does such frame action exist whose behavior could be copied to tabs?
>
> 'display-buffer-reuse-window' together with 'reusable-frames' should
> have all the ingredients for this.  What is missing?

Than we need to add 'reusable-tabs'?

>> Also I use this function in a wrapper that kills the buffer, such wrapper checks
>>
>>    (tab-bar-buffer-visible-in-tabs-p (current-buffer))
>>
>> If true, it doesn't kill the buffer, but buries it.
>
> Via 'kill-buffer-query-functions'?

Rather calling it explicitly with a new commands, but it should be
possible to use 'kill-buffer-query-functions' too.

>> So switching back to that tab still displays the buffer.
>
> When I do 'kill-buffer', I expect that buffer to get removed from
> 'buffer-list' and all windows showing it, that it won't be switched to
> by many functions and so on.  Whatever we'd do, we have to manage this
> controversy somehow.  Think of changes or the deletion of the visited
> files.  How would we try to avoid saving such buffers to their files
> in that case?

A buffer can't be removed from saved window-configurations and window-states.



Reply | Threaded
Open this post in threaded view
|

bug#38354: 27.0.50; Implement display action display-buffer-in-tab

martin rudalics
 >>> display-buffer-in-tab is implemented now, but we need also an action
 >>> to display the buffer in an existing tab if such buffer is
 >>> already displayed in it.
 >>
 >> Could we please clarify the term "display(ed)" in this context.  IIUC
 >> you use it
 >>
 >> - to say that a buffer is part of a tab (a window configuration that
 >>    can be shown on a frame), and
 >
 > In my code I use for this:
 >
 >    (tab-bar-buffer-visible-in-tabs-p buffer)

But this may also return non-nil when the buffer is invisible, that is
not shown in any window.  We already have the "visible frames"
notation, including the non-obscured frames connotation, so I'd rather
not use the term visible in the context of tabs.  Wouldn't something
like 'tab-bar-buffer-present-in-tabs-p' or 'tab-bar-buffer-in-tabs-p'
be more intuitive?

 >> - to say that a buffer is actually displayed on a frame that has a
 >>    tab-bar.
 >
 > In my code I use for this:
 >
 >    (>= (length (get-buffer-window-list buffer t t)) 1)

Why not simply 'get-buffer-window'?  In either case what does the
above "displayed" in

 >>> to display the buffer in an existing tab if such buffer is
 >>> already displayed in it.

refer to now?  The former, the latter or their

 > i.e. I check these situations differently, and use 'or'
 > to combine these conditions:
 >
 >    (or (>= (length (get-buffer-window-list buffer t t)) 1)
 >        (tab-bar-buffer-visible-in-tabs-p buffer))
 >
 > Should these conditions be combined in one function
 > (if the current tab can be considered a tab as well)?

'or'?

 >> Right?  Then the former should not use the term "display(ed)" but
 >> maybe something like "tab(bed)".  "Tab a buffer" would then mean to
 >> make sure that the buffer is part of a tab, "tabbed" that it is part
 >> of at least one tab.
 >
 > Not sure if "tabbed" is the right word.  None of these definitions fits:
 > https://www.dictionary.com/browse/tabbed
 > https://www.urbandictionary.com/define.php?term=tabbed

'tab' itself is already problematic, as we know.

 >>> This will require a new function function tab-bar-buffer-visible-in-tabs.
 >>
 >> What would "visible" precisely stand for here?
 >
 > Maybe a better word is "has"?  Then the name would be tab-bar-has-buffer-in-tab.

OK.  But I think that the "has" is superfluous.

 >> And why "tabs" indiscriminately?  Don't you ever want to check for
 >> presence or visibility in a specific tab only?
 >
 > A specific tab referred by name?  Maybe such function could be useful as well.

Don't you ever want to discriminate the tabs of the selected frame
from the tabs of other frames?  Or are they all the same?

 >> 'display-buffer-reuse-window' together with 'reusable-frames' should
 >> have all the ingredients for this.  What is missing?
 >
 > Than we need to add 'reusable-tabs'?

Why?  If a target tab (a tab with the name specified by ALIST) exists
on any frame specified by 'reusable-frames', reuse it.  Otherwise make
a new frame with the target tab as its only entry.

 >>> Also I use this function in a wrapper that kills the buffer, such wrapper checks
 >>>
 >>>     (tab-bar-buffer-visible-in-tabs-p (current-buffer))
 >>>
 >>> If true, it doesn't kill the buffer, but buries it.
 >>
 >> Via 'kill-buffer-query-functions'?
 >
 > Rather calling it explicitly with a new commands, but it should be
 > possible to use 'kill-buffer-query-functions' too.

But the idea is to _not_ kill the buffer.  Right?  So this might be a
disconcerting effect.

 >>> So switching back to that tab still displays the buffer.
 >>
 >> When I do 'kill-buffer', I expect that buffer to get removed from
 >> 'buffer-list' and all windows showing it, that it won't be switched to
 >> by many functions and so on.  Whatever we'd do, we have to manage this
 >> controversy somehow.  Think of changes or the deletion of the visited
 >> files.  How would we try to avoid saving such buffers to their files
 >> in that case?
 >
 > A buffer can't be removed from saved window-configurations and window-states.

The buffer's object can be removed.  The buffer reference in a saved
window structure is weak, it cannot prevent collecting the buffer
object.

martin



Reply | Threaded
Open this post in threaded view
|

bug#38354: 27.0.50; Implement display action display-buffer-in-tab

Juri Linkov-2
>>    (tab-bar-buffer-visible-in-tabs-p buffer)
>
> But this may also return non-nil when the buffer is invisible, that is
> not shown in any window.  We already have the "visible frames"
> notation, including the non-obscured frames connotation, so I'd rather
> not use the term visible in the context of tabs.

Maybe like having the terms "visible frame" and "iconified frame",
we should use the terms "current tab" and "inactive tabs".

> Wouldn't something like 'tab-bar-buffer-present-in-tabs-p' or
> 'tab-bar-buffer-in-tabs-p' be more intuitive?

Instead of suffix '-p' that assumes the function returns a boolean value,
better to return the found tab with the function name 'tab-bar-buffer-in-tab'.

>>> - to say that a buffer is actually displayed on a frame that has a
>>>    tab-bar.
>>
>> In my code I use for this:
>>
>>    (>= (length (get-buffer-window-list buffer t t)) 1)
>
> Why not simply 'get-buffer-window'?

Because actually I used more complicated logic:

  (> (length (get-buffer-window-list buffer t t)) 1)

that means don't kill the current buffer if it's also displayed
somewhere else.

> In either case what does the above "displayed" in
>
>>>> to display the buffer in an existing tab if such buffer is
>>>> already displayed in it.
>
> refer to now?  The former, the latter or their
>
>> i.e. I check these situations differently, and use 'or'
>> to combine these conditions:
>>
>>    (or (>= (length (get-buffer-window-list buffer t t)) 1)
>>        (tab-bar-buffer-visible-in-tabs-p buffer))
>>
>> Should these conditions be combined in one function
>> (if the current tab can be considered a tab as well)?
>
> 'or'?

Anyway it seems better not to use the word "displayed".

>>> And why "tabs" indiscriminately?  Don't you ever want to check for
>>> presence or visibility in a specific tab only?
>>
>> A specific tab referred by name?  Maybe such function could be useful as well.
>
> Don't you ever want to discriminate the tabs of the selected frame
> from the tabs of other frames?  Or are they all the same?

This means we need to add another dimension: first to look for the buffer
in all tabs of the selected frame, then look in tabs of other frames:
visible, iconified, or on any frame.

>>> 'display-buffer-reuse-window' together with 'reusable-frames' should
>>> have all the ingredients for this.  What is missing?
>>
>> Than we need to add 'reusable-tabs'?
>
> Why?  If a target tab (a tab with the name specified by ALIST) exists
> on any frame specified by 'reusable-frames', reuse it.  Otherwise make
> a new frame with the target tab as its only entry.

I don't understand.  Should ALIST look like this?

(push '("test1" .
        ((display-buffer-reuse-window display-buffer-in-tab)
         (reusable-frames  . visible)
         (name . "Tab1")))
      display-buffer-alist)



Reply | Threaded
Open this post in threaded view
|

bug#38354: 27.0.50; Implement display action display-buffer-in-tab

martin rudalics
 >>>     (tab-bar-buffer-visible-in-tabs-p buffer)
 >>
 >> But this may also return non-nil when the buffer is invisible, that is
 >> not shown in any window.  We already have the "visible frames"
 >> notation, including the non-obscured frames connotation, so I'd rather
 >> not use the term visible in the context of tabs.
 >
 > Maybe like having the terms "visible frame" and "iconified frame",
 > we should use the terms "current tab" and "inactive tabs".

IIUC, during one and the same session any tab is frame-local, can be
shown only on one and the same frame.  Deleting a frame kills its tabs
too, due to our implementation of window configurations.  Right?

If so, then we should decide whether we always want to stick to that
limitation or, eventually allow moving tabs between frames, probably
using window states for that purpose.  And we have to decide whether
such moving of a tab would mean making a completely self-reliant copy
of it or keep properties of it shared among frames.  And eventually we
should decide whether tabs could become first-class citizens - have a
life of their own without being attached to any frame.

Once we have decided on what to do here, we can try finding an
appropriate nomenclature.  That is, for each tab we can then either
find a function called 'tab-frame' (to return its one and only frame)
or 'tab-frame-list' (to return a possibly empty list of all frames
that currently have that tab in their tab-bar).  For each frame, we
should use 'frame-tab(s)-list' for returning all tabs in their
tab-bar, 'frame-selected|current|active-tab' for returning the tab
currently shown on that frame.  Maybe we could use a fitting
ALL-FRAMES arguments to return all tabs in tab-bars of all visible,
visible or iconified, ... frames with a function called 'tab(s)-list'
or the like.

In either case, a suitable, possibly expandable, nomenclature should
be established now - we already have enough examples of clashes in our
present frames/windows/buffer naming conventions.

 >> Wouldn't something like 'tab-bar-buffer-present-in-tabs-p' or
 >> 'tab-bar-buffer-in-tabs-p' be more intuitive?
 >
 > Instead of suffix '-p' that assumes the function returns a boolean value,
 > better to return the found tab with the function name 'tab-bar-buffer-in-tab'.

Sure.  You just have to decide here and now on the arguments of that
function: Which tabs to search for the buffer?

 > Anyway it seems better not to use the word "displayed".

"Displayed" and "shown" are useful for doc-strings and manuals.
Elsewhere, they can be ambiguous.

 >> Don't you ever want to discriminate the tabs of the selected frame
 >> from the tabs of other frames?  Or are they all the same?
 >
 > This means we need to add another dimension: first to look for the buffer
 > in all tabs of the selected frame, then look in tabs of other frames:
 > visible, iconified, or on any frame.

See below.

 >>>> 'display-buffer-reuse-window' together with 'reusable-frames' should
 >>>> have all the ingredients for this.  What is missing?
 >>>
 >>> Than we need to add 'reusable-tabs'?
 >>
 >> Why?  If a target tab (a tab with the name specified by ALIST) exists
 >> on any frame specified by 'reusable-frames', reuse it.  Otherwise make
 >> a new frame with the target tab as its only entry.
 >
 > I don't understand.  Should ALIST look like this?
 >
 > (push '("test1" .
 >          ((display-buffer-reuse-window display-buffer-in-tab)
 >           (reusable-frames  . visible)
 >           (name . "Tab1")))
 >        display-buffer-alist)

What would be the downside of it?  The 'reusable-frames' would specify
the list of frames to investigate - the other dimension you mentioned
above.

BTW: In the manual you write:

   By default, a new tab starts with the current buffer that was current
   before calling the command that adds a new tab.

That's confusing, at least.

martin



Reply | Threaded
Open this post in threaded view
|

bug#38354: 27.0.50; Implement display action display-buffer-in-tab

Juri Linkov-2
>>>>     (tab-bar-buffer-visible-in-tabs-p buffer)

>>>
>>> But this may also return non-nil when the buffer is invisible, that is
>>> not shown in any window.  We already have the "visible frames"
>>> notation, including the non-obscured frames connotation, so I'd rather
>>> not use the term visible in the context of tabs.
>>
>> Maybe like having the terms "visible frame" and "iconified frame",
>> we should use the terms "current tab" and "inactive tabs".
>
> IIUC, during one and the same session any tab is frame-local, can be
> shown only on one and the same frame.  Deleting a frame kills its tabs
> too, due to our implementation of window configurations.  Right?
Right.

> If so, then we should decide whether we always want to stick to that
> limitation or, eventually allow moving tabs between frames, probably
> using window states for that purpose.  And we have to decide whether
> such moving of a tab would mean making a completely self-reliant copy
> of it or keep properties of it shared among frames.  And eventually we
> should decide whether tabs could become first-class citizens - have a
> life of their own without being attached to any frame.

There are two separate cases to consider:

1. Moving an inactive tab to another frame.
   In this case we need to use its window-state, not window-configuration,
   i.e. just delete the window-configuration from tab data, then it
   could be moved to another frame without problems.

2. Moving the current tab to another frame.  Here we need to save
   window-configuration of another frame to its inactive tab, then clone
   the original frame to another frame.  Do you know if such a function
   already exists that duplicates all frame parameters with frame-root
   window-state to another frame?

An additional question to consider is how to interactively select
an another frame: by frame name, or using other-frame with a numeric
prefix argument?

> Once we have decided on what to do here, we can try finding an
> appropriate nomenclature.  That is, for each tab we can then either
> find a function called 'tab-frame' (to return its one and only frame)
> or 'tab-frame-list' (to return a possibly empty list of all frames
> that currently have that tab in their tab-bar).  For each frame, we
> should use 'frame-tab(s)-list' for returning all tabs in their
> tab-bar, 'frame-selected|current|active-tab' for returning the tab
> currently shown on that frame.  Maybe we could use a fitting
> ALL-FRAMES arguments to return all tabs in tab-bars of all visible,
> visible or iconified, ... frames with a function called 'tab(s)-list'
> or the like.
I think a tab should know nothing about the frame where it belongs,
otherwise it would complicate the matter (e.g. saving to the desktop).
The code that operates on tabs should follow the data hierarchy starting
from frames then accessing tabs from each frame, i.e. we should never
have a dangling tab from which to ask where is its frame.

But code that returns the tab could add a parameter frame to the
returned tab like in the patch below.

>>> Wouldn't something like 'tab-bar-buffer-present-in-tabs-p' or
>>> 'tab-bar-buffer-in-tabs-p' be more intuitive?
>>
>> Instead of suffix '-p' that assumes the function returns a boolean value,
>> better to return the found tab with the function name 'tab-bar-buffer-in-tab'.
>
> Sure.  You just have to decide here and now on the arguments of that
> function: Which tabs to search for the buffer?

A better function is implemented below: tab-bar-get-buffer-tab takes
the function name, arguments and docstring from get-buffer-window.

>>>>> 'display-buffer-reuse-window' together with 'reusable-frames' should
>>>>> have all the ingredients for this.  What is missing?
>>>>
>>>> Than we need to add 'reusable-tabs'?
>>>
>>> Why?  If a target tab (a tab with the name specified by ALIST) exists
>>> on any frame specified by 'reusable-frames', reuse it.  Otherwise make
>>> a new frame with the target tab as its only entry.
>>
>> I don't understand.  Should ALIST look like this?
>>
>> (push '("test1" .
>>          ((display-buffer-reuse-window display-buffer-in-tab)
>>           (reusable-frames  . visible)
>>           (name . "Tab1")))
>>        display-buffer-alist)
>
> What would be the downside of it?  The 'reusable-frames' would specify
> the list of frames to investigate - the other dimension you mentioned
> above.
We need an alist entry that will tell display-buffer-reuse-window
to search in tabs.  Maybe just the presence of the 'use-tabs' param
will tell display-buffer-reuse-window to search in tabs like:

(push '("test1" .
         (display-buffer-reuse-window
          (reusable-frames  . visible)
          (use-tabs . t)))
       display-buffer-alist)

> BTW: In the manual you write:
>
>   By default, a new tab starts with the current buffer that was current
>   before calling the command that adds a new tab.
>
> That's confusing, at least.

Maybe this is better?

  By default, a new tab starts with the buffer that was current
  before calling the command that adds a new tab.


diff --git a/lisp/tab-bar.el b/lisp/tab-bar.el
index 5eb332884c..af993637a6 100644
--- a/lisp/tab-bar.el
+++ b/lisp/tab-bar.el
@@ -1284,6 +1284,44 @@ display-buffer-in-tab
               (tab-bar-rename-tab name))))
       (tab-bar-new-tab))))
 
+(defun tab-bar-get-buffer-tab (buffer-or-name &optional all-frames)
+  "Return a tab currently displaying BUFFER-OR-NAME, or nil if none.
+BUFFER-OR-NAME may be a buffer or a buffer name and defaults to
+the current buffer.
+
+The optional argument ALL-FRAMES specifies the frames to consider:
+
+- t means consider all tabs on all existing frames.
+
+- `visible' means consider all tabs on all visible frames.
+
+- A frame means consider all tabs on that frame only.
+
+Any other value of ALL-FRAMES means consider all tabs on the
+selected frame and no others."
+  (let ((buffer (if buffer-or-name
+                    (get-buffer buffer-or-name)
+                  (current-buffer))))
+    (when (bufferp buffer)
+      (let ((frames (cond
+                     ((eq all-frames t) (frame-list))
+                     ((eq all-frames 'visible) (visible-frame-list))
+                     ((framep all-frames) (list all-frames))
+                     (t (list (selected-frame))))))
+        (seq-some (lambda (frame)
+                    (with-selected-frame frame
+                      (seq-some (lambda (tab)
+                                  (when (if (eq (car tab) 'current-tab)
+                                            (get-buffer-window buffer frame)
+                                          (let* ((state (cdr (assq 'ws tab)))
+                                                 (buffers (when state
+                                                            (window-state-buffers state))))
+                                            (or (memq buffer buffers)
+                                                (member (buffer-name buffer) buffers))))
+                                    (append tab `((frame . ,frame)))))
+                                (funcall tab-bar-tabs-function))))
+                  frames)))))
+
 
 (defun switch-to-buffer-other-tab (buffer-or-name &optional norecord)
   "Switch to buffer BUFFER-OR-NAME in another tab.
Reply | Threaded
Open this post in threaded view
|

bug#38354: 27.0.50; Implement display action display-buffer-in-tab

martin rudalics
 >> If so, then we should decide whether we always want to stick to that
 >> limitation or, eventually allow moving tabs between frames, probably
 >> using window states for that purpose.  And we have to decide whether
 >> such moving of a tab would mean making a completely self-reliant copy
 >> of it or keep properties of it shared among frames.  And eventually we
 >> should decide whether tabs could become first-class citizens - have a
 >> life of their own without being attached to any frame.
 >
 > There are two separate cases to consider:
 >
 > 1. Moving an inactive tab to another frame.
 >     In this case we need to use its window-state, not window-configuration,
 >     i.e. just delete the window-configuration from tab data, then it
 >     could be moved to another frame without problems.
 >
 > 2. Moving the current tab to another frame.  Here we need to save
 >     window-configuration of another frame to its inactive tab, then clone
 >     the original frame to another frame.  Do you know if such a function
 >     already exists that duplicates all frame parameters with frame-root
 >     window-state to another frame?

With "moving" I really meant "copying" or "cloning" with the
additional possibility of deleting the original.  So there should be
only one question: Can we simply make a copy of a tab using its window
state (window configurations can't be used because we cannot clone
windows).  If not, what is the problem?

 > An additional question to consider is how to interactively select
 > an another frame: by frame name, or using other-frame with a numeric
 > prefix argument?

How is this question related to tabs?

 >>> (push '("test1" .
 >>>           ((display-buffer-reuse-window display-buffer-in-tab)
 >>>            (reusable-frames  . visible)
 >>>            (name . "Tab1")))
 >>>         display-buffer-alist)
 >>
 >> What would be the downside of it?  The 'reusable-frames' would specify
 >> the list of frames to investigate - the other dimension you mentioned
 >> above.
 >
 > We need an alist entry that will tell display-buffer-reuse-window
 > to search in tabs.  Maybe just the presence of the 'use-tabs' param
 > will tell display-buffer-reuse-window to search in tabs like:
 >
 > (push '("test1" .
 >           (display-buffer-reuse-window
 >            (reusable-frames  . visible)
 >            (use-tabs . t)))
 >         display-buffer-alist)

There is one thing I apparently do not understand yet: When you enable
'tab-bar-mode' it is global - that is any window ever shown on any frame
is also in at least one of that frame's tabs.  Is that right?  So what
would 'use-tabs' mean here when every window is in a tab already?

 >>    By default, a new tab starts with the current buffer that was current
 >>    before calling the command that adds a new tab.
 >>
 >> That's confusing, at least.
 >
 > Maybe this is better?
 >
 >    By default, a new tab starts with the buffer that was current
 >    before calling the command that adds a new tab.

The current buffer is IMHO a much too obscure object to consider here.
Don't you mean the buffer of the selected window?

martin



Reply | Threaded
Open this post in threaded view
|

bug#38354: 27.0.50; Implement display action display-buffer-in-tab

Juri Linkov-2
> With "moving" I really meant "copying" or "cloning" with the
> additional possibility of deleting the original.  So there should be
> only one question: Can we simply make a copy of a tab using its window
> state (window configurations can't be used because we cannot clone
> windows).  If not, what is the problem?

Implemented in the patch below that deletes the window-configuration
from the copied tab, but leaves its window-state.

>> An additional question to consider is how to interactively select
>> an another frame: by frame name, or using other-frame with a numeric
>> prefix argument?
>
> How is this question related to tabs?

Easier to implement than to explain :)  The same patch uses its ARG
the same way as it's used in 'other-frame' to find ARGth frame
where to copy the tab.

>> (push '("test1" .
>>           (display-buffer-reuse-window
>>            (reusable-frames  . visible)
>>            (use-tabs . t)))
>>         display-buffer-alist)
>
> There is one thing I apparently do not understand yet: When you enable
> 'tab-bar-mode' it is global

Do you think we should have 'global-tab-bar-mode' for all frames,
and 'tab-bar-mode' to enable/disable the tab-bar in every frame
separately?

> - that is any window ever shown on any frame is also in at least one
> of that frame's tabs.  Is that right?  So what would 'use-tabs' mean
> here when every window is in a tab already?

These windows in tabs are in window-configurations and window-states.
Now I installed tab-bar-get-buffer-tab that can be used
in display-buffer-reuse-window to search the buffer
in window-states of tabs when use-tabs is non-nil.

>>>    By default, a new tab starts with the current buffer that was current
>>>    before calling the command that adds a new tab.
>>>
>>> That's confusing, at least.
>>
>> Maybe this is better?
>>
>>    By default, a new tab starts with the buffer that was current
>>    before calling the command that adds a new tab.
>
> The current buffer is IMHO a much too obscure object to consider here.
> Don't you mean the buffer of the selected window?
The manual refers to the default value of tab-bar-new-tab-choice
with its docstring:

  If t, start a new tab with the current buffer, i.e. the buffer
  that was current before calling the command that adds a new tab
  (this is the same what `make-frame' does by default).


diff --git a/lisp/tab-bar.el b/lisp/tab-bar.el
index a36be13e37..340f012247 100644
--- a/lisp/tab-bar.el
+++ b/lisp/tab-bar.el
@@ -645,6 +645,33 @@ tab-bar-move-tab
          (to-index (mod (+ from-index arg) (length tabs))))
     (tab-bar-move-tab-to (1+ to-index) (1+ from-index))))
 
+(defun tab-bar-move-tab-to-frame (arg &optional from-frame from-index to-frame to-index)
+  "Move tab from FROM-INDEX position to new position at TO-INDEX.
+FROM-INDEX defaults to the current tab index.
+FROM-INDEX and TO-INDEX count from 1.
+FROM-FRAME specifies the source frame and defaults to the selected frame.
+TO-FRAME specifies the target frame and defaults the next frame.
+Interactively, ARG selects the ARGth different frame to move to."
+  (interactive "P")
+  (unless from-frame
+    (setq from-frame (selected-frame)))
+  (unless to-frame
+    (dotimes (_ (prefix-numeric-value arg))
+      (setq to-frame (next-frame to-frame))))
+  (unless (eq from-frame to-frame)
+    (let* ((from-tabs (with-selected-frame from-frame
+                        (funcall tab-bar-tabs-function)))
+           (from-index (or from-index (1+ (tab-bar--current-tab-index from-tabs))))
+           (from-tab (nth (1- from-index) from-tabs))
+           (to-tabs (with-selected-frame to-frame
+                      (funcall tab-bar-tabs-function)))
+           (to-index (max 0 (min (1- (or to-index 1)) (1- (length to-tabs))))))
+      (setq from-tabs (delq from-tab from-tabs))
+      (cl-pushnew (assq-delete-all 'wc from-tab) (nthcdr to-index to-tabs))
+      (set-frame-parameter from-frame 'tabs from-tabs)
+      (set-frame-parameter to-frame 'tabs to-tabs)
+      (force-mode-line-update t))))
+
 
 (defcustom tab-bar-new-tab-to 'right
   "Defines where to create a new tab.
Reply | Threaded
Open this post in threaded view
|

bug#38354: 27.0.50; Implement display action display-buffer-in-tab

martin rudalics
 >> There is one thing I apparently do not understand yet: When you enable
 >> 'tab-bar-mode' it is global
 >
 > Do you think we should have 'global-tab-bar-mode' for all frames,
 > and 'tab-bar-mode' to enable/disable the tab-bar in every frame
 > separately?

In the sense that we can enable menu or tool bars for frames
individually?  I think so, yes.

 >> - that is any window ever shown on any frame is also in at least one
 >> of that frame's tabs.  Is that right?  So what would 'use-tabs' mean
 >> here when every window is in a tab already?
 >
 > These windows in tabs are in window-configurations and window-states.
 > Now I installed tab-bar-get-buffer-tab

"Return a tab whose window contains BUFFER-OR-NAME, or nil if none."

is a bit misleading, maybe

"Return a tab with a|owning a window whose buffer is BUFFER-OR-NAME."

is better.  Also the 'lambda's following the 'seq-some' should be
moved to a new line to keep line lengths within their limits.

In either case, do I read the code correctly that it can prefer a
window in a non-current tab to a window in a current tab on another
frame.  If so, do we want that?

An aside: Are you sure that 'tab-bar-tabs' should always work on the
selected frame and not take a frame as argument?

 > that can be used
 > in display-buffer-reuse-window to search the buffer
 > in window-states of tabs when use-tabs is non-nil.

Given my observation above, this can make a tab current in frame A
although the current tab of frame B already shows the buffer.  Right?

 >>>     By default, a new tab starts with the buffer that was current
 >>>     before calling the command that adds a new tab.
 >>
 >> The current buffer is IMHO a much too obscure object to consider here.
 >> Don't you mean the buffer of the selected window?
 >
 > The manual refers to the default value of tab-bar-new-tab-choice
 > with its docstring:
 >
 >    If t, start a new tab with the current buffer, i.e. the buffer
 >    that was current before calling the command that adds a new tab
 >    (this is the same what `make-frame' does by default).

OK.  Then my next nitpick is that the doc-string of
'tab-bar-new-tab-choice' says

If the value is a string, use it as a buffer name switch to a buffer

which apparently should have "... name.  Switch ..." instead.

martin



Reply | Threaded
Open this post in threaded view
|

bug#38354: 27.0.50; Implement display action display-buffer-in-tab

Juri Linkov-2
>>> There is one thing I apparently do not understand yet: When you enable
>>> 'tab-bar-mode' it is global
>>
>> Do you think we should have 'global-tab-bar-mode' for all frames,
>> and 'tab-bar-mode' to enable/disable the tab-bar in every frame
>> separately?
>
> In the sense that we can enable menu or tool bars for frames
> individually?  I think so, yes.

Oh, this also means redesigning tool-bar-mode and menu-bar-mode?
Then I'm not sure.  But generally sounds like the right thing to do
sometime.

>>> - that is any window ever shown on any frame is also in at least one
>>> of that frame's tabs.  Is that right?  So what would 'use-tabs' mean
>>> here when every window is in a tab already?
>>
>> These windows in tabs are in window-configurations and window-states.
>> Now I installed tab-bar-get-buffer-tab
>
> "Return a tab whose window contains BUFFER-OR-NAME, or nil if none."
>
> is a bit misleading, maybe
>
> "Return a tab with a|owning a window whose buffer is BUFFER-OR-NAME."

Done.

> is better.  Also the 'lambda's following the 'seq-some' should be
> moved to a new line to keep line lengths within their limits.

Done.

> In either case, do I read the code correctly that it can prefer a
> window in a non-current tab to a window in a current tab on another
> frame.  If so, do we want that?

Do you think it should return a list of all tabs owning a window
with the buffer?

> An aside: Are you sure that 'tab-bar-tabs' should always work on the
> selected frame and not take a frame as argument?

Thanks for the idea, implemented.

>> that can be used
>> in display-buffer-reuse-window to search the buffer
>> in window-states of tabs when use-tabs is non-nil.
>
> Given my observation above, this can make a tab current in frame A
> although the current tab of frame B already shows the buffer.  Right?

Why not?  Does display-buffer-reuse-window currently prefers
a window on the same frame even when a window on another frame
also shows the same buffer?

>>    If t, start a new tab with the current buffer, i.e. the buffer
>>    that was current before calling the command that adds a new tab
>>    (this is the same what `make-frame' does by default).
>
> OK.  Then my next nitpick is that the doc-string of
> 'tab-bar-new-tab-choice' says
>
> If the value is a string, use it as a buffer name switch to a buffer
>
> which apparently should have "... name.  Switch ..." instead.

Fixed.

Additional question: using quit-window on the buffer displayed
by display-buffer-in-tab should close its tab.  Could you recommend
how to implement this?  Maybe to add an additional argument CLOSE-TAB
to quit-restore-window?  e.g.

  (defun quit-restore-window (&optional window bury-or-kill close-tab)
  ...

For testing I tried to call '(tab-bar-close-tab)' at the end of
'quit-restore-window' unconditionally, but sometimes it does wrong thing:
when quitting the last window of the frame closes the frame,
'(tab-bar-close-tab)' closes the tab on another frame that
is selected after closing the original frame.



Reply | Threaded
Open this post in threaded view
|

bug#38354: 27.0.50; Implement display action display-buffer-in-tab

martin rudalics
 >>> Do you think we should have 'global-tab-bar-mode' for all frames,
 >>> and 'tab-bar-mode' to enable/disable the tab-bar in every frame
 >>> separately?
 >>
 >> In the sense that we can enable menu or tool bars for frames
 >> individually?  I think so, yes.
 >
 > Oh, this also means redesigning tool-bar-mode and menu-bar-mode?

By no means.  I meant to add a 'tab-bar-lines' entry to the list of
frame parameters so one can override the standard behavior for
individual frames as one can do with the ‘menu-bar-lines’ and
'tool-bar-lines' parameters.  (Obviously, that naming convention is
silly because one cannot use it to change the number of lines these
objects have.)

 > Then I'm not sure.  But generally sounds like the right thing to do
 > sometime.
[...]
 >> In either case, do I read the code correctly that it can prefer a
 >> window in a non-current tab to a window in a current tab on another
 >> frame.  If so, do we want that?
 >
 > Do you think it should return a list of all tabs owning a window
 > with the buffer?

No.  I meant that it should continue to scan frames as long as it has
not found a window showing that buffer.  During that scan it would
record the first tab that has a window whose buffer is the one searched
for.  The return value would then be preferably (the tab of) a window
actually showing the buffer or, if no such window exists, a tab that
has a window whose buffer is the one searched for.

 > Why not?  Does display-buffer-reuse-window currently prefers
 > a window on the same frame even when a window on another frame
 > also shows the same buffer?

It does.  But I don't say that 'display-buffer-in-tab' must do the
same.  What I meant was, as sketched above, to prefer a tab where a
window already shows the buffer to tabs that would show the buffer but
only if they were current/active or tabs where such a buffer window
would have to be added first.

 > Additional question: using quit-window on the buffer displayed
 > by display-buffer-in-tab should close its tab.  Could you recommend
 > how to implement this?  Maybe to add an additional argument CLOSE-TAB
 > to quit-restore-window?  e.g.
 >
 >    (defun quit-restore-window (&optional window bury-or-kill close-tab)
 >    ...
 >
 > For testing I tried to call '(tab-bar-close-tab)' at the end of
 > 'quit-restore-window' unconditionally, but sometimes it does wrong thing:
 > when quitting the last window of the frame closes the frame,
 > '(tab-bar-close-tab)' closes the tab on another frame that
 > is selected after closing the original frame.

Then 'tab-bar-close-tab' is probably the wrong function to call here.

But why do you want to close a tab (an entire window configuration)
here?  Shouldn't 'quit-restore-window' just remove that window from a
tab, that is, update the current tab of that frame to what it is?  Or
does the current tab of a frame not necessarily match that frame's
window configuration?

martin




Reply | Threaded
Open this post in threaded view
|

bug#38354: 27.0.50; Implement display action display-buffer-in-tab

Juri Linkov-2
>>> In either case, do I read the code correctly that it can prefer a
>>> window in a non-current tab to a window in a current tab on another
>>> frame.  If so, do we want that?
>>
>> Do you think it should return a list of all tabs owning a window
>> with the buffer?
>
> No.  I meant that it should continue to scan frames as long as it has
> not found a window showing that buffer.  During that scan it would
> record the first tab that has a window whose buffer is the one searched
> for.  The return value would then be preferably (the tab of) a window
> actually showing the buffer or, if no such window exists, a tab that
> has a window whose buffer is the one searched for.
>
>> Why not?  Does display-buffer-reuse-window currently prefers
>> a window on the same frame even when a window on another frame
>> also shows the same buffer?
>
> It does.  But I don't say that 'display-buffer-in-tab' must do the
> same.  What I meant was, as sketched above, to prefer a tab where a
> window already shows the buffer to tabs that would show the buffer but
> only if they were current/active or tabs where such a buffer window
> would have to be added first.

I have no preference, but don't understand why the implementation
should provide only one way, but not another.  Is it what users expect?

>> Additional question: using quit-window on the buffer displayed
>> by display-buffer-in-tab should close its tab.  Could you recommend
>> how to implement this?  Maybe to add an additional argument CLOSE-TAB
>> to quit-restore-window?  e.g.
>>
>>    (defun quit-restore-window (&optional window bury-or-kill close-tab)
>>    ...
>>
>> For testing I tried to call '(tab-bar-close-tab)' at the end of
>> 'quit-restore-window' unconditionally, but sometimes it does wrong thing:
>> when quitting the last window of the frame closes the frame,
>> '(tab-bar-close-tab)' closes the tab on another frame that
>> is selected after closing the original frame.
>
> Then 'tab-bar-close-tab' is probably the wrong function to call here.
>
> But why do you want to close a tab (an entire window configuration)
> here?  Shouldn't 'quit-restore-window' just remove that window from a
> tab, that is, update the current tab of that frame to what it is?  Or
> does the current tab of a frame not necessarily match that frame's
> window configuration?

It should close the tab only on quitting the window with the buffer
whose displaying created the tab.  For example, when
'switch-to-buffer-other-tab' creates a new tab, then quitting
its buffer should close the tab.

This is the same logic as for creating/deleting the window, e.g.
'C-h f car RET' creates a window, but typing 'q' in Help buffer
deletes the window.  'q' in a new tab should close it as well.



Reply | Threaded
Open this post in threaded view
|

bug#38354: 27.0.50; Implement display action display-buffer-in-tab

martin rudalics
 > I have no preference, but don't understand why the implementation
 > should provide only one way, but not another.  Is it what users expect?

IIRC that's how we interpreted the term "reuse" in the past - do not
change configurations if we can avoid it.  But I have no preference
either.

 > It should close the tab only on quitting the window with the buffer
 > whose displaying created the tab.  For example, when
 > 'switch-to-buffer-other-tab' creates a new tab, then quitting
 > its buffer should close the tab.
 >
 > This is the same logic as for creating/deleting the window, e.g.
 > 'C-h f car RET' creates a window, but typing 'q' in Help buffer
 > deletes the window.  'q' in a new tab should close it as well.

OK.  But what if another action adds something to such a tab and we
then quit the window.  Or is such a scenario impossible?

martin



Reply | Threaded
Open this post in threaded view
|

bug#38354: 27.0.50; Implement display action display-buffer-in-tab

Juri Linkov-2
>> I have no preference, but don't understand why the implementation
>> should provide only one way, but not another.  Is it what users expect?
>
> IIRC that's how we interpreted the term "reuse" in the past - do not
> change configurations if we can avoid it.  But I have no preference
> either.

Still unclear what window currently code prefers in case of two
or more windows displaying the same buffer, or when the buffer
is displayed in windows of two or more frames.  What window/frame it
reuses?  Just the first found?  Is it possible to express such
preferences for multiple windows/frames in display-buffer-alist?

>> It should close the tab only on quitting the window with the buffer
>> whose displaying created the tab.  For example, when
>> 'switch-to-buffer-other-tab' creates a new tab, then quitting
>> its buffer should close the tab.
>>
>> This is the same logic as for creating/deleting the window, e.g.
>> 'C-h f car RET' creates a window, but typing 'q' in Help buffer
>> deletes the window.  'q' in a new tab should close it as well.
>
> OK.  But what if another action adds something to such a tab and we
> then quit the window.  Or is such a scenario impossible?

Does a similar situation happen in case of window quitting?
Then the same logic could be reused for tab quitting.
When another buffer is displayed in the same window,
then quit-window doesn't delete the window with another buffer?



Reply | Threaded
Open this post in threaded view
|

bug#38354: 27.0.50; Implement display action display-buffer-in-tab

martin rudalics
 > Still unclear what window currently code prefers in case of two
 > or more windows displaying the same buffer, or when the buffer
 > is displayed in windows of two or more frames.  What window/frame it
 > reuses?  Just the first found?

The order is determined by 'get-buffer-window-list' which in its turn
relies on 'window-list-1'.

 > Is it possible to express such
 > preferences for multiple windows/frames in display-buffer-alist?

It tries the selected window first and a window on the selected frame
afterwards.  If both fail it respects 'reusable-frames'.

 >> OK.  But what if another action adds something to such a tab and we
 >> then quit the window.  Or is such a scenario impossible?
 >
 > Does a similar situation happen in case of window quitting?
 > Then the same logic could be reused for tab quitting.
 > When another buffer is displayed in the same window,
 > then quit-window doesn't delete the window with another buffer?

Correct.  'quit-restore-window' is quite sensitive wrt such changes
and refrains from doing more drastic things (like deleting a window or
frame) when the user has switched buffers in between.

martin