Repository: qutebrowser/qutebrowser

Issue:
Tab numbers are ordered alphabetically with new completion
In the `:buffer` completion, tabs are ordered like `0/1`, `0/10`, `0/11`, `0/2` if there are >10. This wasn't the case with v0.11.0.

cc @rcorre 


Discussion:

Resolving patch:
diff --git a/qutebrowser/completion/models/configmodel.py b/qutebrowser/completion/models/configmodel.py
--- a/qutebrowser/completion/models/configmodel.py
+++ b/qutebrowser/completion/models/configmodel.py
@@ -29,7 +29,7 @@ def section():
     model = completionmodel.CompletionModel(column_widths=(20, 70, 10))
     sections = ((name, configdata.SECTION_DESC[name].splitlines()[0].strip())
                 for name in configdata.DATA)
-    model.add_category(listcategory.ListCategory("Sections", sections))
+    model.add_category(listcategory.ListCategory("Sections", sorted(sections)))
     return model
 
 
@@ -57,7 +57,7 @@ def option(sectname):
         config = objreg.get('config')
         val = config.get(sectname, name, raw=True)
         options.append((name, desc, val))
-    model.add_category(listcategory.ListCategory(sectname, options))
+    model.add_category(listcategory.ListCategory(sectname, sorted(options)))
     return model
 
 
@@ -92,5 +92,6 @@ def value(sectname, optname):
         [(current, "Current value"), (default, "Default value")])
     model.add_category(cur_cat)
     if vals is not None:
-        model.add_category(listcategory.ListCategory("Completions", vals))
+        model.add_category(listcategory.ListCategory("Completions",
+                                                     sorted(vals)))
     return model
diff --git a/qutebrowser/completion/models/listcategory.py b/qutebrowser/completion/models/listcategory.py
--- a/qutebrowser/completion/models/listcategory.py
+++ b/qutebrowser/completion/models/listcategory.py
@@ -60,33 +60,19 @@ def set_pattern(self, val):
         sortcol = 0
         self.sort(sortcol)
 
-    def lessThan(self, lindex, rindex):
+    def lessThan(self, _lindex, rindex):
         """Custom sorting implementation.
 
-        Prefers all items which start with self._pattern. Other than that, uses
-        normal Python string sorting.
+        Prefers all items which start with self._pattern. Other than that, keep
+        items in their original order.
 
         Args:
-            lindex: The QModelIndex of the left item (*left* < right)
+            _lindex: The QModelIndex of the left item (*left* < right)
             rindex: The QModelIndex of the right item (left < *right*)
 
         Return:
             True if left < right, else False
         """
-        qtutils.ensure_valid(lindex)
         qtutils.ensure_valid(rindex)
-
-        left = self.srcmodel.data(lindex)
         right = self.srcmodel.data(rindex)
-
-        leftstart = left.startswith(self._pattern)
-        rightstart = right.startswith(self._pattern)
-
-        if leftstart and rightstart:
-            return left < right
-        elif leftstart:
-            return True
-        elif rightstart:
-            return False
-        else:
-            return left < right
+        return not right.startswith(self._pattern)
diff --git a/qutebrowser/completion/models/miscmodels.py b/qutebrowser/completion/models/miscmodels.py
--- a/qutebrowser/completion/models/miscmodels.py
+++ b/qutebrowser/completion/models/miscmodels.py
@@ -54,7 +54,7 @@ def helptopic():
             settings.append((name, desc))
 
     model.add_category(listcategory.ListCategory("Commands", cmdlist))
-    model.add_category(listcategory.ListCategory("Settings", settings))
+    model.add_category(listcategory.ListCategory("Settings", sorted(settings)))
     return model
 
 
@@ -180,4 +180,4 @@ def _get_cmd_completions(include_hidden, include_aliases, prefix=''):
             bindings = ', '.join(cmd_to_keys.get(name, []))
             cmdlist.append((name, "Alias for '{}'".format(cmd), bindings))
 
-    return cmdlist
+    return sorted(cmdlist)


Test patch:
diff --git a/tests/unit/completion/test_listcategory.py b/tests/unit/completion/test_listcategory.py
--- a/tests/unit/completion/test_listcategory.py
+++ b/tests/unit/completion/test_listcategory.py
@@ -31,7 +31,7 @@
 
     ('foo',
      [('foob', ''), ('fooc', ''), ('fooa', '')],
-     [('fooa', ''), ('foob', ''), ('fooc', '')]),
+     [('foob', ''), ('fooc', ''), ('fooa', '')]),
 
     # prefer foobar as it starts with the pattern
     ('foo',
diff --git a/tests/unit/completion/test_models.py b/tests/unit/completion/test_models.py
--- a/tests/unit/completion/test_models.py
+++ b/tests/unit/completion/test_models.py
@@ -119,8 +119,8 @@ def quickmarks(quickmark_manager_stub):
     """Pre-populate the quickmark-manager stub with some quickmarks."""
     quickmark_manager_stub.marks = collections.OrderedDict([
         ('aw', 'https://wiki.archlinux.org'),
-        ('ddg', 'https://duckduckgo.com'),
         ('wiki', 'https://wikipedia.org'),
+        ('ddg', 'https://duckduckgo.com'),
     ])
     return quickmark_manager_stub
 
@@ -246,16 +246,16 @@ def test_quickmark_completion(qtmodeltester, quickmarks):
     _check_completions(model, {
         "Quickmarks": [
             ('aw', 'https://wiki.archlinux.org', None),
-            ('ddg', 'https://duckduckgo.com', None),
             ('wiki', 'https://wikipedia.org', None),
+            ('ddg', 'https://duckduckgo.com', None),
         ]
     })
 
 
 @pytest.mark.parametrize('row, removed', [
     (0, 'aw'),
-    (1, 'ddg'),
-    (2, 'wiki'),
+    (1, 'wiki'),
+    (2, 'ddg'),
 ])
 def test_quickmark_completion_delete(qtmodeltester, quickmarks, row, removed):
     """Test deleting a quickmark from the quickmark completion model."""
@@ -282,17 +282,17 @@ def test_bookmark_completion(qtmodeltester, bookmarks):
 
     _check_completions(model, {
         "Bookmarks": [
-            ('http://qutebrowser.org', 'qutebrowser | qutebrowser', None),
             ('https://github.com', 'GitHub', None),
             ('https://python.org', 'Welcome to Python.org', None),
+            ('http://qutebrowser.org', 'qutebrowser | qutebrowser', None),
         ]
     })
 
 
 @pytest.mark.parametrize('row, removed', [
-    (0, 'http://qutebrowser.org'),
-    (1, 'https://github.com'),
-    (2, 'https://python.org'),
+    (0, 'https://github.com'),
+    (1, 'https://python.org'),
+    (2, 'http://qutebrowser.org'),
 ])
 def test_bookmark_completion_delete(qtmodeltester, bookmarks, row, removed):
     """Test deleting a quickmark from the quickmark completion model."""
@@ -326,14 +326,14 @@ def test_url_completion(qtmodeltester, web_history_populated,
 
     _check_completions(model, {
         "Quickmarks": [
-            ('https://duckduckgo.com', 'ddg', None),
             ('https://wiki.archlinux.org', 'aw', None),
             ('https://wikipedia.org', 'wiki', None),
+            ('https://duckduckgo.com', 'ddg', None),
         ],
         "Bookmarks": [
-            ('http://qutebrowser.org', 'qutebrowser | qutebrowser', None),
             ('https://github.com', 'GitHub', None),
             ('https://python.org', 'Welcome to Python.org', None),
+            ('http://qutebrowser.org', 'qutebrowser | qutebrowser', None),
         ],
         "History": [
             ('https://github.com', 'https://github.com', '2016-05-01'),
@@ -385,12 +385,12 @@ def test_url_completion_delete_bookmark(qtmodeltester, bookmarks,
 
     # sanity checks
     assert model.data(parent) == "Bookmarks"
-    assert model.data(idx) == 'https://github.com'
+    assert model.data(idx) == 'https://python.org'
     assert 'https://github.com' in bookmarks.marks
 
     len_before = len(bookmarks.marks)
     model.delete_cur_item(idx)
-    assert 'https://github.com' not in bookmarks.marks
+    assert 'https://python.org' not in bookmarks.marks
     assert len_before == len(bookmarks.marks) + 1
 
 
@@ -408,12 +408,12 @@ def test_url_completion_delete_quickmark(qtmodeltester,
 
     # sanity checks
     assert model.data(parent) == "Quickmarks"
-    assert model.data(idx) == 'https://duckduckgo.com'
+    assert model.data(idx) == 'https://wiki.archlinux.org'
     assert 'ddg' in quickmarks.marks
 
     len_before = len(quickmarks.marks)
     model.delete_cur_item(idx)
-    assert 'ddg' not in quickmarks.marks
+    assert 'aw' not in quickmarks.marks
     assert len_before == len(quickmarks.marks) + 1
 
 
@@ -456,9 +456,9 @@ def test_session_completion(qtmodeltester, session_manager_stub):
     qtmodeltester.check(model)
 
     _check_completions(model, {
-        "Sessions": [('1', None, None),
-                     ('2', None, None),
-                     ('default', None, None)]
+        "Sessions": [('default', None, None),
+                     ('1', None, None),
+                     ('2', None, None)]
     })