From 581d7659baa1ebe6f2f0fde6f6e847dcceef282a Mon Sep 17 00:00:00 2001 From: Ryan Roden-Corrent Date: Fri, 9 Sep 2016 07:26:24 -0400 Subject: [PATCH] Clean up Completer.on_selection_changed. Remove the dependency on the class variables _empty_item_index and _cursor_part to make the code easier to follow. If _update_completion is refactored in a similar way these variables can be removed. --- qutebrowser/completion/completer.py | 52 +++++++++++++++---------- tests/unit/completion/test_completer.py | 3 +- 2 files changed, 32 insertions(+), 23 deletions(-) diff --git a/qutebrowser/completion/completer.py b/qutebrowser/completion/completer.py index 11d9abe0e..b9f7b711f 100644 --- a/qutebrowser/completion/completer.py +++ b/qutebrowser/completion/completer.py @@ -179,6 +179,27 @@ class Completer(QObject): else: return s + def _partition(self): + parts = [x for x in self._split(keep=True) if x] + 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 + log.completion.debug('empty partition result') + return [], '', [] + @pyqtSlot(QItemSelection) def on_selection_changed(self, selected): """Change the completed part if a new item was selected. @@ -355,29 +376,18 @@ class Completer(QObject): including a trailing space and we shouldn't continue completing the current item. """ - parts = self._split() - log.completion.debug("changing part {} to '{}'".format( - self._cursor_part, newtext)) - try: - parts[self._cursor_part] = newtext - except IndexError: - parts.append(newtext) - # We want to place the cursor directly after the part we just changed. - cursor_str = self._cmd.prefix() + ' '.join( - parts[:self._cursor_part + 1]) - if immediate: - # If we should complete immediately, we want to move the cursor by - # one more char, to get to the next field. - cursor_str += ' ' - text = self._cmd.prefix() + ' '.join(parts) - if immediate and self._cursor_part == len(parts) - 1: - # If we should complete immediately and we're completing the last - # part in the commandline, we automatically add a space. + before, center, after = self._partition() + log.completion.debug("changing {} to '{}'".format(center, newtext)) + text = self._cmd.prefix() + ' '.join([*before, newtext]) + pos = len(text) + (1 if immediate else 0) + if after: + text += ' ' + ' '.join(after) + elif immediate: + # pad with a space if quick-completing the last entry text += ' ' + log.completion.debug("setting text = '{}', pos = {}".format(text, pos)) self._cmd.setText(text) - log.completion.debug("Placing cursor after '{}'".format(cursor_str)) - log.modes.debug("Completion triggered, focusing {!r}".format(self)) - self._cmd.setCursorPosition(len(cursor_str)) + self._cmd.setCursorPosition(pos) self._cmd.setFocus() self._cmd.show_cmd.emit() diff --git a/tests/unit/completion/test_completer.py b/tests/unit/completion/test_completer.py index cb8b49d8a..5532e4400 100644 --- a/tests/unit/completion/test_completer.py +++ b/tests/unit/completion/test_completer.py @@ -204,6 +204,7 @@ def test_update_completion(txt, expected, status_command_stub, completer_obj, (':|set', 'set', ':set|'), (':|set ', 'set', ':set|'), (':|se', 'set', ':set|'), + (':|se ', 'set', ':set|'), (':s|e', 'set', ':set|'), (':se|', 'set', ':set|'), (':|se fonts', 'set', ':set| fonts'), @@ -248,8 +249,6 @@ def test_on_selection_changed(before, newtxt, after, completer_obj, config_stub.data['completion']['quick-complete'] = quick_complete model.count = unittest.mock.Mock(return_value=count) _set_cmd_prompt(status_command_stub, before) - # TODO: refactor so cursor_part is a local rather than class variable - completer_obj._update_cursor_part() completer_obj.on_selection_changed(selection) model.data.assert_called_with(indexes[0]) assert status_command_stub.text() == expected_txt