Repository: qutebrowser/qutebrowser

Issue:
Edit command using ${editor}
**Feature request:** Provide a way to edit the current command using one's `${editor}`, similar to:

- vim: `c_^F` / `q:`
- bash: `edit-and-execute-command`
- zsh: `edit-command-line`

Qutebrowser already has `edit-url`, which launches `${editor}` to edit a url (default: current url), so this would be similar to that. Call it `edit-command`, for instance.

**Example 1:**

- `: <execute some really long command>`
- Get error (e.g. for a `:bind` or `:set`), or realize I used the wrong search engine or forgot some option or whatever.
- `:<C-p>` (go back to previous command)
- `<C-f>` (open command in editor, where `<C-f>` is bound to `edit-command`)
- fix command, save, quit, and execute command

In this example, `edit-command` is called in command mode, just like `c_^F` in vim.

**Example 2:**

- I have a url in my clipboard, which I want to open in a new tab, but I need to edit the url before opening it.
- `q:` (open editor, where `q:` is bound to `edit-command2`; see below)
- Type `open -t `, paste my url, edit it however I want, save, quit, and execute command

In this example, `edit-command2` is called in normal mode, just like `q:` in vim.

I wrote `edit-command2` to make it clear that it's slightly different from `edit-command`, in the sense that `edit-command` is invoked in command mode, while `edit-command2` is invoked in normal mode (just like `edit-url` currently).

Also, I'd be happy even if there were *only* the command-mode version, because example 2 could just as well go like this:

- `:open -t <paste url>` (but don't execute)
- `<C-f>` (i.e. `<edit-command>`)
- edit the url, save, quit, and execute command

**Rationale:** The point is that editing a command in vim (or whatever) would be way easier/faster than ctrl-arrowing across words or trying to remember emacs-style shortcuts like `<C-a>` and so on.


Discussion:
I made a bash userscript for this which you can get [here](https://raw.githubusercontent.com/pkill-nine/qutebrowser/qutebrowser-custom/misc/userscripts/command-run). Personally I think it shouldn't require a userscript and should be native, but it works fine.

Just bind it with something like `:bind --force --mode command <ctrl-e> spawn --userscript commandline_edit.sh`, where commandline_edit.sh is the name of the userscript. Then pressing ctrl+e opens up vim with the contents of the commandline which you can then change.
Thanks, that userscript works great! Just one issue (specific to binding `<ctrl-e>` in `command` mode):

Namely, `--mode command` does not work for key(chain)s like `<ctrl-e>` that are defined by default in the `[command,prompt]` section of `keys.conf`:

    :bind --mode command <ctrl-e> spawn --userscript edit-command
    Duplicate keychain <ctrl-e> - use --force to override!

    :bind --force --mode command <ctrl-e> spawn --userscript edit-command
    Can't find binding '<ctrl-e>' in section 'command'!

The "solution" is to use `--mode command,prompt`. However, this makes `<ctrl-e>` available for use even in `prompt` mode, which it should not be.

I tried manually deleting

    rl-end-of-line
        <ctrl-e>

from the `[command,prompt]` section of `keys.conf` and adding

    spawn --userscript edit-command
        <ctrl-e>

to the `[command]` section. However, on restarting qutebrowser,

    rl-end-of-line
        <ctrl-e>

gets automatically added *back* to `[command,prompt]`, which overrides the custom `<ctrl-e>` binding under `[command]`.

So, the only solution is to use `--mode command,prompt` / to put the custom `<ctrl-e>` binding under `[command,prompt]`.
@pkill-nine Here's a slight improvement that puts the command prefix, e.g. `:`, into a variable, then `echo`-es it back. This way, the userscript works not only with regular commands (prefixed by `:`), but also searches, prefixed by `/` or `?`:

```bash
#!/bin/bash
#
# Edit command-line text. See #2453
# <https://github.com/qutebrowser/qutebrowser/issues/2453>.
# Adapted from @pkill-nine's userscript.

trap cleanup EXIT

cleanup() {
    rm "$qb_cmd"
}

qb_cmd=$(mktemp --tmpdir qutebrowser_XXXXXXXX)
cmd_prefix=$(echo "$QUTE_COMMANDLINE_TEXT" | head -c1)
echo "$QUTE_COMMANDLINE_TEXT" | cut -c 2- > "$qb_cmd"
urxvt -e vim -n "$qb_cmd"

echo set-cmd-text "$cmd_prefix$(head -n1 "$qb_cmd")" >> "$QUTE_FIFO"
```
Since various people seem to want this, I'm okay with adding this in qutebrowser itself.

It should be quite straightforward to do, anyone wants to contribute a PR for it? :wink:

@brianbuccola as for the keybinding issue, try:

```
<unbound>
    <ctrl-e>
```

Resolving patch:
diff --git a/qutebrowser/mainwindow/statusbar/command.py b/qutebrowser/mainwindow/statusbar/command.py
--- a/qutebrowser/mainwindow/statusbar/command.py
+++ b/qutebrowser/mainwindow/statusbar/command.py
@@ -24,7 +24,7 @@
 
 from qutebrowser.keyinput import modeman, modeparsers
 from qutebrowser.commands import cmdexc, cmdutils
-from qutebrowser.misc import cmdhistory
+from qutebrowser.misc import cmdhistory, editor
 from qutebrowser.misc import miscwidgets as misc
 from qutebrowser.utils import usertypes, log, objreg
 
@@ -166,6 +166,23 @@ def command_accept(self):
         modeman.leave(self._win_id, usertypes.KeyMode.command, 'cmd accept')
         self.got_cmd[str].emit(prefixes[text[0]] + text[1:])
 
+    @cmdutils.register(instance='status-command', scope='window')
+    def edit_command(self, run=False):
+        """Open an editor to modify the current command.
+
+        Args:
+            run: Run the command if the editor exits successfully.
+        """
+        ed = editor.ExternalEditor(parent=self)
+
+        def callback(text):
+            self.set_cmd_text(text)
+            if run:
+                self.got_cmd[str].emit(text)
+
+        ed.editing_finished.connect(callback)
+        ed.edit(self.text())
+
     @pyqtSlot(usertypes.KeyMode)
     def on_mode_left(self, mode):
         """Clear up when command mode was left.


Test patch:
diff --git a/tests/end2end/features/editor.feature b/tests/end2end/features/editor.feature
--- a/tests/end2end/features/editor.feature
+++ b/tests/end2end/features/editor.feature
@@ -141,3 +141,11 @@ Feature: Opening external editors
         And I wait for "Read back: bar" in the log
         And I run :click-element id qute-button
         Then the javascript message "text: bar" should be logged
+
+    ## :edit-command
+
+    Scenario: Edit a command and run it
+        When I run :set-cmd-text :message-info foo
+        And I set up a fake editor replacing "foo" by "bar"
+        And I run :edit-command --run
+        Then the message "bar" should be shown