From dc655dd40bb0782aed3766cd75ae0e1803846546 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 27 May 2014 17:21:29 +0200 Subject: [PATCH] First try at using cursor position --- qutebrowser/widgets/_completion.py | 37 ++++++------- qutebrowser/widgets/statusbar/_command.py | 65 +++++++++++++++++------ 2 files changed, 68 insertions(+), 34 deletions(-) diff --git a/qutebrowser/widgets/_completion.py b/qutebrowser/widgets/_completion.py index 6ece502bc..3f2adf7bf 100644 --- a/qutebrowser/widgets/_completion.py +++ b/qutebrowser/widgets/_completion.py @@ -65,6 +65,7 @@ class CompletionView(QTreeView): Signals: change_completed_part: Text which should be substituted for the word we're currently completing. + arg: The text to change to. """ STYLESHEET = """ @@ -201,13 +202,16 @@ class CompletionView(QTreeView): # Item is a real item, not a category header -> success return idx - def _get_new_completion(self, parts): + def _get_new_completion(self, parts, cursor_part): """Get a new completion model. - parts: The command chunks to get a completion for. + Args: + parts: The command chunks to get a completion for. + cursor_part: The part the cursor is over currently. """ - if len(parts) == 1: - # parts = [''] or something like ['set'] + logger.debug("cursor part: {}".format(cursor_part)) + if cursor_part == 0: + # '|' or 'set|' return self._models['command'] # delegate completion to command try: @@ -220,19 +224,18 @@ class CompletionView(QTreeView): # command without any available completions return None try: - idx = len(parts[1:]) - 1 + idx = cursor_part - 1 completion_name = completions[idx] - logger.debug('partlen: {}, modelname {}'.format( - len(parts[1:]), completion_name)) + logger.debug('modelname {}'.format(completion_name)) except IndexError: # More arguments than completions return None if completion_name == 'option': - section = parts[-2] + section = parts[cursor_part - 1] model = self._models['option'].get(section) elif completion_name == 'value': - section = parts[-3] - option = parts[-2] + section = parts[cursor_part - 2] + option = parts[cursor_part - 1] try: model = self._models['value'][section][option] except KeyError: @@ -249,9 +252,6 @@ class CompletionView(QTreeView): Args: prev: True for prev item, False for next one. - - Emit: - change_completed_part: When a completion took place. """ if not self._completing: # No completion running at the moment, ignore keypress @@ -283,16 +283,16 @@ class CompletionView(QTreeView): elif section == 'aliases': self._init_command_completion() - @pyqtSlot(str) - def on_update_completion(self, text): + @pyqtSlot(str, int) + def on_update_completion(self, text, cursor_part): """Check if completions are available and activate them. Slot for the textChanged signal of the statusbar command widget. Args: text: The new text + cursor_part: The part the cursor is currently over. """ - # FIXME we should also consider the cursor position if not text.startswith(':'): # This is a search or gibberish, so we don't need to complete # anything (yet) @@ -304,7 +304,7 @@ class CompletionView(QTreeView): text = text.lstrip(':') parts = split_cmdline(text) - model = self._get_new_completion(parts) + model = self._get_new_completion(parts, cursor_part) if model is None: logger.debug("No completion model for {}.".format(parts)) else: @@ -324,7 +324,8 @@ class CompletionView(QTreeView): if model is None: return - pattern = parts[-1] if parts else '' + pattern = parts[cursor_part] if parts else '' + logger.debug("pattern: {}".format(pattern)) self._model.pattern = pattern self._model.srcmodel.mark_all_items(pattern) if self._enabled: diff --git a/qutebrowser/widgets/statusbar/_command.py b/qutebrowser/widgets/statusbar/_command.py index 90ada4a12..81c6364c2 100644 --- a/qutebrowser/widgets/statusbar/_command.py +++ b/qutebrowser/widgets/statusbar/_command.py @@ -50,7 +50,8 @@ class Command(MinimalLineEdit): hidden. hide_completion: Emitted when the completion widget should be hidden. update_completion: Emitted when the completion should be shown/updated. - arg: The new text which was set. + arg 0: The new text which was set. + arg 1: The part the cursor is currently in. show_cmd: Emitted when command input should be shown. hide_cmd: Emitted when command input can be hidden. """ @@ -60,7 +61,7 @@ class Command(MinimalLineEdit): got_search_rev = pyqtSignal(str) clear_completion_selection = pyqtSignal() hide_completion = pyqtSignal() - update_completion = pyqtSignal(str) + update_completion = pyqtSignal(str, int) show_cmd = pyqtSignal() hide_cmd = pyqtSignal() @@ -73,10 +74,35 @@ class Command(MinimalLineEdit): self.history = History() self._validator = _CommandValidator(self) self.setValidator(self._validator) - self.textEdited.connect(self.history.stop) - self.textEdited.connect(self.update_completion) + self.textEdited.connect(self.on_text_edited) + self.cursorPositionChanged.connect(self.on_cursor_position_changed) self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Ignored) + def _cursor_part(self): + """Get the part index of the commandline where the cursor is over.""" + prefix, parts = self._split() + cursor_pos = self.cursorPosition() + cursor_pos -= len(prefix) + for i, part in enumerate(parts): + logger.debug("part {}, len {}, pos {}".format(i, len(part), + cursor_pos)) + if cursor_pos <= len(part): + # foo| bar + return i + cursor_pos -= (len(part) + 1) # FIXME are spaces always 1 char? + + def _split(self): + text = self.text() + if not text: + return '', [] + if text[0] in STARTCHARS: + prefix = text[0] + text = text[1:] + else: + prefix = '' + parts = split_cmdline(text) + return prefix, parts + @pyqtSlot(str) def set_cmd_text(self, text): """Preset the statusbar to some text. @@ -91,7 +117,7 @@ class Command(MinimalLineEdit): self.setText(text) if old_text != text: # We want the completion to pop out here. - self.update_completion.emit(text) + self.update_completion.emit(text, self._cursor_part()) self.setFocus() self.show_cmd.emit() @@ -102,18 +128,14 @@ class Command(MinimalLineEdit): Args: text: The text to set (string). """ - # FIXME we should consider the cursor position. - text = self.text() - if text[0] in STARTCHARS: - prefix = text[0] - text = text[1:] - else: - prefix = '' - parts = split_cmdline(text) - logger.debug("Old text: '{}' - parts: {}, changing to '{}'".format( - text, parts, newtext)) - parts[-1] = newtext + prefix, parts = self._split() + cursor_part = self._cursor_part() + logger.debug("parts: {}, changing {} to '{}'".format( + parts, cursor_part, newtext)) + parts[cursor_part] = newtext + self.blockSignals(True) self.setText(prefix + ' '.join(parts)) + self.blockSignals(False) self.setFocus() self.show_cmd.emit() @@ -165,6 +187,17 @@ class Command(MinimalLineEdit): if text[0] in signals: signals[text[0]].emit(text.lstrip(text[0])) + @pyqtSlot(str) + def on_text_edited(self, text): + """Slot for textEdited. Stop history and update completion.""" + self.history.stop() + self.update_completion.emit(text, self._cursor_part()) + + @pyqtSlot(int, int) + def on_cursor_position_changed(self, _old, _new): + """Slot for cursorPositionChanged to update completion.""" + self.update_completion.emit(self.text(), self._cursor_part()) + def on_mode_left(self, mode): """Clear up when ommand mode was left.