First try at using cursor position

This commit is contained in:
Florian Bruhin 2014-05-27 17:21:29 +02:00
parent 4d0649a825
commit dc655dd40b
2 changed files with 68 additions and 34 deletions

View File

@ -65,6 +65,7 @@ class CompletionView(QTreeView):
Signals: Signals:
change_completed_part: Text which should be substituted for the word change_completed_part: Text which should be substituted for the word
we're currently completing. we're currently completing.
arg: The text to change to.
""" """
STYLESHEET = """ STYLESHEET = """
@ -201,13 +202,16 @@ class CompletionView(QTreeView):
# Item is a real item, not a category header -> success # Item is a real item, not a category header -> success
return idx return idx
def _get_new_completion(self, parts): def _get_new_completion(self, parts, cursor_part):
"""Get a new completion model. """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: logger.debug("cursor part: {}".format(cursor_part))
# parts = [''] or something like ['set'] if cursor_part == 0:
# '|' or 'set|'
return self._models['command'] return self._models['command']
# delegate completion to command # delegate completion to command
try: try:
@ -220,19 +224,18 @@ class CompletionView(QTreeView):
# command without any available completions # command without any available completions
return None return None
try: try:
idx = len(parts[1:]) - 1 idx = cursor_part - 1
completion_name = completions[idx] completion_name = completions[idx]
logger.debug('partlen: {}, modelname {}'.format( logger.debug('modelname {}'.format(completion_name))
len(parts[1:]), completion_name))
except IndexError: except IndexError:
# More arguments than completions # More arguments than completions
return None return None
if completion_name == 'option': if completion_name == 'option':
section = parts[-2] section = parts[cursor_part - 1]
model = self._models['option'].get(section) model = self._models['option'].get(section)
elif completion_name == 'value': elif completion_name == 'value':
section = parts[-3] section = parts[cursor_part - 2]
option = parts[-2] option = parts[cursor_part - 1]
try: try:
model = self._models['value'][section][option] model = self._models['value'][section][option]
except KeyError: except KeyError:
@ -249,9 +252,6 @@ class CompletionView(QTreeView):
Args: Args:
prev: True for prev item, False for next one. prev: True for prev item, False for next one.
Emit:
change_completed_part: When a completion took place.
""" """
if not self._completing: if not self._completing:
# No completion running at the moment, ignore keypress # No completion running at the moment, ignore keypress
@ -283,16 +283,16 @@ class CompletionView(QTreeView):
elif section == 'aliases': elif section == 'aliases':
self._init_command_completion() self._init_command_completion()
@pyqtSlot(str) @pyqtSlot(str, int)
def on_update_completion(self, text): def on_update_completion(self, text, cursor_part):
"""Check if completions are available and activate them. """Check if completions are available and activate them.
Slot for the textChanged signal of the statusbar command widget. Slot for the textChanged signal of the statusbar command widget.
Args: Args:
text: The new text 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(':'): if not text.startswith(':'):
# This is a search or gibberish, so we don't need to complete # This is a search or gibberish, so we don't need to complete
# anything (yet) # anything (yet)
@ -304,7 +304,7 @@ class CompletionView(QTreeView):
text = text.lstrip(':') text = text.lstrip(':')
parts = split_cmdline(text) parts = split_cmdline(text)
model = self._get_new_completion(parts) model = self._get_new_completion(parts, cursor_part)
if model is None: if model is None:
logger.debug("No completion model for {}.".format(parts)) logger.debug("No completion model for {}.".format(parts))
else: else:
@ -324,7 +324,8 @@ class CompletionView(QTreeView):
if model is None: if model is None:
return 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.pattern = pattern
self._model.srcmodel.mark_all_items(pattern) self._model.srcmodel.mark_all_items(pattern)
if self._enabled: if self._enabled:

View File

@ -50,7 +50,8 @@ class Command(MinimalLineEdit):
hidden. hidden.
hide_completion: Emitted when the completion widget should be hidden. hide_completion: Emitted when the completion widget should be hidden.
update_completion: Emitted when the completion should be shown/updated. 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. show_cmd: Emitted when command input should be shown.
hide_cmd: Emitted when command input can be hidden. hide_cmd: Emitted when command input can be hidden.
""" """
@ -60,7 +61,7 @@ class Command(MinimalLineEdit):
got_search_rev = pyqtSignal(str) got_search_rev = pyqtSignal(str)
clear_completion_selection = pyqtSignal() clear_completion_selection = pyqtSignal()
hide_completion = pyqtSignal() hide_completion = pyqtSignal()
update_completion = pyqtSignal(str) update_completion = pyqtSignal(str, int)
show_cmd = pyqtSignal() show_cmd = pyqtSignal()
hide_cmd = pyqtSignal() hide_cmd = pyqtSignal()
@ -73,10 +74,35 @@ class Command(MinimalLineEdit):
self.history = History() self.history = History()
self._validator = _CommandValidator(self) self._validator = _CommandValidator(self)
self.setValidator(self._validator) self.setValidator(self._validator)
self.textEdited.connect(self.history.stop) self.textEdited.connect(self.on_text_edited)
self.textEdited.connect(self.update_completion) self.cursorPositionChanged.connect(self.on_cursor_position_changed)
self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Ignored) 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) @pyqtSlot(str)
def set_cmd_text(self, text): def set_cmd_text(self, text):
"""Preset the statusbar to some text. """Preset the statusbar to some text.
@ -91,7 +117,7 @@ class Command(MinimalLineEdit):
self.setText(text) self.setText(text)
if old_text != text: if old_text != text:
# We want the completion to pop out here. # We want the completion to pop out here.
self.update_completion.emit(text) self.update_completion.emit(text, self._cursor_part())
self.setFocus() self.setFocus()
self.show_cmd.emit() self.show_cmd.emit()
@ -102,18 +128,14 @@ class Command(MinimalLineEdit):
Args: Args:
text: The text to set (string). text: The text to set (string).
""" """
# FIXME we should consider the cursor position. prefix, parts = self._split()
text = self.text() cursor_part = self._cursor_part()
if text[0] in STARTCHARS: logger.debug("parts: {}, changing {} to '{}'".format(
prefix = text[0] parts, cursor_part, newtext))
text = text[1:] parts[cursor_part] = newtext
else: self.blockSignals(True)
prefix = ''
parts = split_cmdline(text)
logger.debug("Old text: '{}' - parts: {}, changing to '{}'".format(
text, parts, newtext))
parts[-1] = newtext
self.setText(prefix + ' '.join(parts)) self.setText(prefix + ' '.join(parts))
self.blockSignals(False)
self.setFocus() self.setFocus()
self.show_cmd.emit() self.show_cmd.emit()
@ -165,6 +187,17 @@ class Command(MinimalLineEdit):
if text[0] in signals: if text[0] in signals:
signals[text[0]].emit(text.lstrip(text[0])) 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): def on_mode_left(self, mode):
"""Clear up when ommand mode was left. """Clear up when ommand mode was left.