Repository: qutebrowser/qutebrowser

Issue:
Add start/end arguments to :tab-move
Hello. Thanks for everything!

By the way, I'm very sorry, but if it's possible, could you please make the default behavior of :tab-move with no argument "move to last tab" like :tabmove in vim?

Also, it seems that :tab-move 1 is used to move to the first tab, not :tab-move 0.
I'm guessing that's how it was made to match Chrome's Alt+1 key binding.

How about the idea of brushing up the specification, at least for the parts where the tab behavior is inconsistent, using the following vim help?

- https://vimhelp.org/tabpage.txt.html

I hope you will consider it.


Discussion:
> Hello. Thanks for everything!

You're welcome!

> could you please make the default behavior of :tab-move with no argument "move to last tab" like :tabmove in vim?

I'd rather not, as this would break the behavior of `gm` with no count, which currently moves a tab to the very left. I'd rather keep it that way.

However, I think it'd be good to have something like `start` and `end` as arguments to `:tab-move`, which I assume will do what you want. I'll rename the issue accordingly.

> Also, it seems that :tab-move 1 is used to move to the first tab, not :tab-move 0. I'm guessing that's how it was made to match Chrome's Alt+1 key binding.

Mainly because qutebrowser's tab indices start at 1.

> How about the idea of brushing up the specification, at least for the parts where the tab behavior is inconsistent, using the following vim help?
> 
> * https://vimhelp.org/tabpage.txt.html

In general, qutebrowser is inspired by vim, but having 1:1 compatibility isn't a goal - sometimes there are more intuitive solutions (or just solutions more suitable for a browser, or more consistent with other things qutebrowser is doing, etc.).

Thanks for considering it.
You're right, the form of the tab numbers starting from 1 is intuitive.

How about making the key binding of ":tab-move last" as gM?
No strong feeling either way. Given that this has never come up so far, I don't think it needs a default keybinding, but on the other hand I don't know what else we'd use `gM` for.
That's because I wanted to move the new tabs opened between the group of pinned tabs on the left edge of the tab bar to the right edge for later reading together.

Resolving patch:
diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py
--- a/qutebrowser/browser/commands.py
+++ b/qutebrowser/browser/commands.py
@@ -1004,11 +1004,10 @@ def tab_focus(self, index: Union[str, int] = None,
             raise cmdutils.CommandError("There's no tab with index {}!".format(
                 index))
 
-    @cmdutils.register(instance='command-dispatcher', scope='window')
-    @cmdutils.argument('index', choices=['+', '-'])
-    @cmdutils.argument('count', value=cmdutils.Value.count)
-    def tab_move(self, index: Union[str, int] = None,
-                 count: int = None) -> None:
+    @cmdutils.register(instance="command-dispatcher", scope="window")
+    @cmdutils.argument("index", choices=["+", "-", "start", "end"])
+    @cmdutils.argument("count", value=cmdutils.Value.count)
+    def tab_move(self, index: Union[str, int] = None, count: int = None) -> None:
         """Move the current tab according to the argument and [count].
 
         If neither is given, move it to the first position.
@@ -1017,24 +1016,29 @@ def tab_move(self, index: Union[str, int] = None,
             index: `+` or `-` to move relative to the current tab by
                    count, or a default of 1 space.
                    A tab index to move to that index.
+                   `start` and `end` to move to the start and the end
             count: If moving relatively: Offset.
                    If moving absolutely: New position (default: 0). This
                    overrides the index argument, if given.
         """
-        if index in ['+', '-']:
+        if index in ["+", "-"]:
             # relative moving
             new_idx = self._current_index()
             delta = 1 if count is None else count
-            if index == '-':
+            if index == "-":
                 new_idx -= delta
-            elif index == '+':  # pragma: no branch
+            elif index == "+":  # pragma: no branch
                 new_idx += delta
 
             if config.val.tabs.wrap:
                 new_idx %= self._count()
         else:
             # absolute moving
-            if count is not None:
+            if index == "start":
+                new_idx = 0
+            elif index == "end":
+                new_idx = self._count() - 1
+            elif count is not None:
                 new_idx = count - 1
             elif index is not None:
                 assert isinstance(index, int)


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
@@ -633,6 +633,27 @@ Feature: Tab management
             - data/numbers/1.txt (active)
             - data/numbers/3.txt
 
+    Scenario: :tab-move with absolute position
+        When I open data/numbers/1.txt
+        And I open data/numbers/2.txt in a new tab
+        And I open data/numbers/3.txt in a new tab
+        And I run :tab-focus 1
+        And I run :tab-move end
+        Then the following tabs should be open:
+            - data/numbers/2.txt
+            - data/numbers/3.txt
+            - data/numbers/1.txt (active)
+
+    Scenario: :tab-move with absolute position
+        When I open data/numbers/1.txt
+        And I open data/numbers/2.txt in a new tab
+        And I open data/numbers/3.txt in a new tab
+        And I run :tab-move start
+        Then the following tabs should be open:
+            - data/numbers/3.txt (active)
+            - data/numbers/1.txt
+            - data/numbers/2.txt
+
     Scenario: Make sure :tab-move retains metadata
         When I open data/title.html
         And I open data/hello.txt in a new tab