Repository: qutebrowser/qutebrowser

Issue:
Crash when pasting emoji into the command line
qutebrowser crashes when I paste 🌀 into the command line.

```
01:01:57 DEBUG    completion completer:_partition:163 partitioning ['🌀'] around position 2
01:01:57 ERROR    misc       crashsignal:exception_hook:211 Uncaught exception
Traceback (most recent call last):
  File "/home/ring/software/qutebrowser/qutebrowser/completion/completer.py", line 247, in _update_completion
    before_cursor, pattern, after_cursor = self._partition()
TypeError: 'NoneType' object is not iterable
```

I've tracked down the problem up to a point:

``` python
    def _partition(self):
        """Divide the commandline text into chunks around the cursor position.

        Return:
            ([parts_before_cursor], 'part_under_cursor', [parts_after_cursor])
        """
        text = self._cmd.text()[len(self._cmd.prefix()):]
        if not text or not text.strip():
            # Only ":", empty part under the cursor with nothing before/after
            return [], '', []
        runner = runners.CommandRunner(self._win_id)
        result = runner.parse(text, fallback=True, keep=True)
        parts = [x for x in result.cmdline if x]
        print(self._cmd.cursorPosition())
        pos = self._cmd.cursorPosition() - len(self._cmd.prefix())
        log.completion.debug('partitioning {} around position {}'.format(parts,
                                                                         pos))
        for i, part in enumerate(parts):
            pos -= len(part)
            if pos <= 0:
                if part[pos-1:pos+1].isspace():
                    # cursor is in a space between two existing words
                    parts.insert(i, '')
                prefix = [x.strip() for x in parts[:i]]
                center = parts[i].strip()
                # strip trailing whitepsace included as a separate token
                postfix = [x.strip() for x in parts[i+1:] if not x.isspace()]
                log.completion.debug(
                    "partitioned: {} '{}' {}".format(prefix, center, postfix))
                return prefix, center, postfix
```

When I paste the emoji into an empty command line `self._cmd.cursorPosition` returns 3, which is too high. This means `pos` is too high, so the loop terminates before `pos` becomes negative, so the function implicitly returns `None`.

Version info:

```
qutebrowser v0.8.1
Git commit: v0.8.0-1379-g4801352-dirty (2016-10-03 07:03:19 +0200)

CPython: 3.4.2
Qt: 5.3.2
PyQt: 5.3.2

sip: 4.16.4
colorama: 0.3.2
pypeg2: 2.15
jinja2: 2.7.3
pygments: 2.0.1
yaml: 3.11
cssutils: no
typing: no
PyQt5.QtWebEngineWidgets: no
pdf.js: 1.4.20 (bundled)
Webkit: 538.1
SSL: OpenSSL 1.0.1t  3 May 2016

Style: QFusionStyle
Platform: Linux-4.6.0-0.bpo.1-amd64-x86_64-with-debian-8.6, 64bit
Frozen: False
Imported from /home/ring/software/qutebrowser/qutebrowser
OS Version: 

--- /etc/os-release ---
PRETTY_NAME="Debian GNU/Linux 8 (jessie)"
NAME="Debian GNU/Linux"
VERSION_ID="8"
VERSION="8 (jessie)"
ID=debian
```



Discussion:
That character is [U+1F300](http://www.fileformat.info/info/unicode/char/1f300/index.htm) which is probably interesting because it's two UTF-16 characters.

I'm guessing this makes Qt return 3 as cursor position, which might or might not be a Qt bug?

@rcorre any idea what we can do about this? Do we just assume the cursor is at the rightmost position when the cursor position is higher than the length of the string in Python?


Resolving patch:
diff --git a/qutebrowser/completion/completer.py b/qutebrowser/completion/completer.py
--- a/qutebrowser/completion/completer.py
+++ b/qutebrowser/completion/completer.py
@@ -157,6 +157,7 @@ def _partition(self):
         result = runner.parse(text, fallback=True, keep=True)
         parts = [x for x in result.cmdline if x]
         pos = self._cmd.cursorPosition() - len(self._cmd.prefix())
+        pos = min(pos, len(text))
         log.completion.debug('partitioning {} around position {}'.format(parts,
                                                                          pos))
         for i, part in enumerate(parts):


Test patch:
diff --git a/tests/end2end/features/completion.feature b/tests/end2end/features/completion.feature
--- a/tests/end2end/features/completion.feature
+++ b/tests/end2end/features/completion.feature
@@ -22,3 +22,8 @@ Feature: Command bar completion
         # Make sure qutebrowser doesn't hang
         And I run :message-info "Still alive!"
         Then the message "Still alive!" should be shown
+
+    Scenario: Crash when pasting emoji into the command line (#2007)
+        Given I open about:blank
+        When I run :set-cmd-text -s :🌀
+        Then no crash should happen