Repository: qutebrowser/qutebrowser
Pull request: Close commandline on backspace / when leading prefix gets deleted.
closes https://github.com/qutebrowser/qutebrowser/issues/3458

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/qutebrowser/qutebrowser/3478)
<!-- Reviewable:end -->


Problem statement:
close commandline on backspace
I use:
qutebrowser v1.0.4
Git commit: f5edd4941 (2017-12-30 18:33:15 +0100)
Backend: QtWebEngine (Chromium 61.0.3163.140)

In vim if i delete the leading colon in the command line with backspace, it closes the command line. It would be nice to have the same behaviour in qutebrowser.
Thanks

Hints from issue discussion:

Commit message: option to close commandline by deleting the prefix

Patch to review:
diff --git a/qutebrowser/config/configdata.yml b/qutebrowser/config/configdata.yml
index a118a8b59f5..a3ae79e2dab 100644
--- a/qutebrowser/config/configdata.yml
+++ b/qutebrowser/config/configdata.yml
@@ -740,6 +740,11 @@ completion.use_best_match:
   default: false
   desc: Execute the best-matching command on a partial match.
 
+completion.exit_prefix:
+  type: Bool
+  default: false
+  desc: Exit command mode if the prefix gets deleted.
+
 ## downloads
 
 downloads.location.directory:
@@ -2493,4 +2498,4 @@ bindings.commands:
       keyboard.
 
     * register: Entered when qutebrowser is waiting for a register name/key for
-      commands like `:set-mark`.
+      commands like `:set-mark`.
\ No newline at end of file
diff --git a/qutebrowser/mainwindow/statusbar/command.py b/qutebrowser/mainwindow/statusbar/command.py
index ab8ac7b5d50..145ef380895 100644
--- a/qutebrowser/mainwindow/statusbar/command.py
+++ b/qutebrowser/mainwindow/statusbar/command.py
@@ -31,7 +31,6 @@
 
 
 class Command(misc.MinimalLineEditMixin, misc.CommandLineEdit):
-
     """The commandline part of the statusbar.
 
     Attributes:
@@ -55,6 +54,8 @@ class Command(misc.MinimalLineEditMixin, misc.CommandLineEdit):
     show_cmd = pyqtSignal()
     hide_cmd = pyqtSignal()
 
+    prefixes = '/ : ?'
+
     def __init__(self, *, win_id, private, parent=None):
         misc.CommandLineEdit.__init__(self, parent=parent)
         misc.MinimalLineEditMixin.__init__(self)
@@ -68,6 +69,7 @@ def __init__(self, *, win_id, private, parent=None):
         self.textChanged.connect(self.update_completion)
         self.textChanged.connect(self.updateGeometry)
         self.textChanged.connect(self._incremental_search)
+        self.textChanged.connect(self._exit_prefix)
 
     def prefix(self):
         """Get the currently entered command prefix."""
@@ -219,7 +221,10 @@ def setText(self, text):
         if not text:
             pass
         elif text[0] in modeparsers.STARTCHARS:
-            super().set_prompt(text[0])
+            if config.val.completion.exit_prefix:
+                super().set_prompt('')
+            else:
+                super().set_prompt(text[0])
         else:
             raise utils.Unreachable("setText got called with invalid text "
                                     "'{}'!".format(text))
@@ -259,3 +264,9 @@ def _incremental_search(self, text):
 
         if self.prefix() in ['/', '?']:
             self.got_cmd[str].emit(search_prefixes[text[0]] + text[1:])
+
+    @pyqtSlot(str)
+    def _exit_prefix(self, text):
+        if config.val.completion.exit_prefix and (not text or text[0] not in self.prefixes):
+            modeman.leave(self._win_id, usertypes.KeyMode.command,
+                          'prefix deleted', maybe=True)


Code review comments:
diff_hunk: @@ -740,6 +740,11 @@ completion.use_best_match:
   default: false
   desc: Execute the best-matching command on a partial match.
 
+completion.exit_prefix:
path: qutebrowser/config/configdata.yml
text: @user1:
Do you think it really makes sense to add a setting for this?

diff_hunk: @@ -219,7 +221,10 @@ def setText(self, text):
         if not text:
             pass
         elif text[0] in modeparsers.STARTCHARS:
-            super().set_prompt(text[0])
+            if config.val.completion.exit_prefix:
+                super().set_prompt('')
path: qutebrowser/mainwindow/statusbar/command.py
text: @user1:
This doesn't work correctly - for example, pressing `<Home>` now goes to the beginning of the line. Instead, you can probably remove `miscwidgets._CommandValidator`.

diff_hunk: @@ -55,6 +54,8 @@ class Command(misc.MinimalLineEditMixin, misc.CommandLineEdit):
     show_cmd = pyqtSignal()
     hide_cmd = pyqtSignal()
 
+    prefixes = '/ : ?'
+
path: qutebrowser/mainwindow/statusbar/command.py
text: @user1:
You can just use `modeparsers.STARTCHARS`.

diff_hunk: @@ -259,3 +264,9 @@ def _incremental_search(self, text):
 
         if self.prefix() in ['/', '?']:
             self.got_cmd[str].emit(search_prefixes[text[0]] + text[1:])
+
+    @pyqtSlot(str)
+    def _exit_prefix(self, text):
+        if config.val.completion.exit_prefix and (not text or text[0] not in self.prefixes):
+            modeman.leave(self._win_id, usertypes.KeyMode.command,
+                          'prefix deleted', maybe=True)
path: qutebrowser/mainwindow/statusbar/command.py
text: @user1:
Why `maybe=True`? If this gets called, we're guaranteed to be in command mode, right?

@author:
Without this it crashes after executing a command.

diff_hunk: @@ -259,3 +264,9 @@ def _incremental_search(self, text):
 
         if self.prefix() in ['/', '?']:
             self.got_cmd[str].emit(search_prefixes[text[0]] + text[1:])
+
+    @pyqtSlot(str)
+    def _exit_prefix(self, text):
+        if config.val.completion.exit_prefix and (not text or text[0] not in self.prefixes):
path: qutebrowser/mainwindow/statusbar/command.py
text: @user1:
How can `text[0] not in self.prefixes` happen? Wouldn't it always be completely empty?

@author:
In case the user only deletes the prefix and leaves the rest of the command. Otherwise if you delete the prefix only and try to execute the command it crashes.