Repository: qutebrowser/qutebrowser

Issue:
Answering 'no' to :tab-only prompt with pinned tabs should close non-pinned tabs
If anyone prefers the current behavior of `<no>` when running `:tab-only`, please let me know here!

This will change the behavior of `<no>` to 'close all non-pinned tabs except the current one' instead of 'don't close any tabs'.

```
<hdqn> [20:54:21] :tab-only prompts to close pinned tabs and fails
    completely if denied
<tyil> [20:54:34] The-Compiler: yeah, its weird, bu tall I wanted was
    a working qutebrowser :p
<The-Compiler> [20:54:49] hdqn: I think that's intended
<hdqn> [20:55:05] I suggest at least having a setting for this - I
    expected to keep the current tab and all pinned tabs
<The-Compiler> [20:55:47] jgkamat: what do you think? ^
<hdqn> [20:59:18] 'pinned' is generally considered to mean 'immune to
    auto-moving/sorting/deletion', 'fixed in place', and there is
    literally 0 sane reason to keep :tab-only atomic
<hdqn> [21:00:29] of course I'm aware that I can just submit a PR, so
    I'll leave that here for you guys to consider ^-^
<hdqn> [21:00:50] qb is making my life so much easier <3
<***> Playback Complete.
<jgkamat> hmmmmmmmmm
<jgkamat> hdqn: so you think that if you said 'no' to the prompt, it
    should delete all non-pinned tabs?
<jgkamat> adding a setting would be easy but I don't think it would be
    needed (I don't think people would mind what happened too much). I
    would personally prefer changing the <no> behavior if that's what
    people wanted
<The-Compiler> I don't mind much either way, but I think hdqn has a
    point
<The-Compiler> "oh, I have pinned tabs, now I don't want to close
    anything at all" seems to make less sense to me than "hm, okay, I
    want to keep the pinned ones but still close the rest"
```


Discussion:

Resolving patch:
diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py
--- a/qutebrowser/browser/commands.py
+++ b/qutebrowser/browser/commands.py
@@ -953,22 +953,25 @@ def _to_close(i):
                         (prev and i < cur_idx) or
                         (next_ and i > cur_idx))
 
-        # Check to see if we are closing any pinned tabs
-        if not force:
-            for i, tab in enumerate(self._tabbed_browser.widgets()):
-                if _to_close(i) and tab.data.pinned:
-                    self._tabbed_browser.tab_close_prompt_if_pinned(
-                        tab,
-                        force,
-                        lambda: self.tab_only(
-                            prev=prev, next_=next_, force=True))
-                    return
-
+        # close as many tabs as we can
         first_tab = True
+        pinned_tabs_cleanup = False
         for i, tab in enumerate(self._tabbed_browser.widgets()):
             if _to_close(i):
-                self._tabbed_browser.close_tab(tab, new_undo=first_tab)
-                first_tab = False
+                if force or not tab.data.pinned:
+                    self._tabbed_browser.close_tab(tab, new_undo=first_tab)
+                    first_tab = False
+                else:
+                    pinned_tabs_cleanup = tab
+
+        # Check to see if we would like to close any pinned tabs
+        if pinned_tabs_cleanup:
+            self._tabbed_browser.tab_close_prompt_if_pinned(
+                pinned_tabs_cleanup,
+                force,
+                lambda: self.tab_only(
+                    prev=prev, next_=next_, force=True),
+                text="Are you sure you want to close pinned tabs?")
 
     @cmdutils.register(instance='command-dispatcher', scope='window')
     def undo(self):
diff --git a/qutebrowser/mainwindow/tabbedbrowser.py b/qutebrowser/mainwindow/tabbedbrowser.py
--- a/qutebrowser/mainwindow/tabbedbrowser.py
+++ b/qutebrowser/mainwindow/tabbedbrowser.py
@@ -256,7 +256,9 @@ def shutdown(self):
         for tab in self.widgets():
             self._remove_tab(tab)
 
-    def tab_close_prompt_if_pinned(self, tab, force, yes_action):
+    def tab_close_prompt_if_pinned(
+            self, tab, force, yes_action,
+            text="Are you sure you want to close a pinned tab?"):
         """Helper method for tab_close.
 
         If tab is pinned, prompt. If not, run yes_action.
@@ -265,7 +267,7 @@ def tab_close_prompt_if_pinned(self, tab, force, yes_action):
         if tab.data.pinned and not force:
             message.confirm_async(
                 title='Pinned Tab',
-                text="Are you sure you want to close a pinned tab?",
+                text=text,
                 yes_action=yes_action, default=False, abort_on=[tab.destroyed])
         else:
             yes_action()


Test patch:
diff --git a/tests/end2end/features/tabs.feature b/tests/end2end/features/tabs.feature
--- a/tests/end2end/features/tabs.feature
+++ b/tests/end2end/features/tabs.feature
@@ -1183,7 +1183,7 @@ Feature: Tab management
         And I run :tab-pin
         And I run :tab-next
         And I run :tab-only
-        And I wait for "*want to close a pinned tab*" in the log
+        And I wait for "*want to close pinned tabs*" in the log
         And I run :prompt-accept yes
         Then the following tabs should be open:
             - data/numbers/1.txt (active) (pinned)
@@ -1195,7 +1195,7 @@ Feature: Tab management
         And I run :tab-pin
         And I run :tab-next
         And I run :tab-only
-        And I wait for "*want to close a pinned tab*" in the log
+        And I wait for "*want to close pinned tabs*" in the log
         And I run :prompt-accept no
         Then the following tabs should be open:
             - data/numbers/1.txt (active) (pinned)