From c0236b8d229b33eca8b9aa30e5af3fc550053c47 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 17 Apr 2014 11:08:14 +0200 Subject: [PATCH] Use a proper separated model for cmd history --- qutebrowser/models/cmdhistory.py | 113 +++++++++++++++++++++++++++++++ qutebrowser/widgets/statusbar.py | 75 +++++--------------- 2 files changed, 131 insertions(+), 57 deletions(-) create mode 100644 qutebrowser/models/cmdhistory.py diff --git a/qutebrowser/models/cmdhistory.py b/qutebrowser/models/cmdhistory.py new file mode 100644 index 000000000..1b534d5ac --- /dev/null +++ b/qutebrowser/models/cmdhistory.py @@ -0,0 +1,113 @@ +# Copyright 2014 Florian Bruhin (The Compiler) +# +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see . + +"""Command history for the status bar.""" + +import logging + +from PyQt5.QtCore import pyqtSlot + +import qutebrowser.config.config as config +from qutebrowser.utils.usertypes import NeighborList + + +class HistoryEmptyError(Exception): + + """Raised when the history is empty.""" + + pass + + +class HistoryEndReachedError(Exception): + + """Raised when the end of the history is reached.""" + + pass + + +class History: + + """Command history. + + Attributes: + browsing: If we're currently browsing the history (property). + _history: A list of executed commands, with newer commands at the end. + _tmphist: Temporary history for history browsing (as NeighborList) + """ + + def __init__(self): + self._tmphist = None + if config.cmd_history.data is None: + self._history = [] + else: + self._history = config.cmd_history.data + + @property + def browsing(self): + """Check _tmphist to see if we're browsing.""" + return self._tmphist is not None + + def start(self, text): + """Start browsing to the history. + + Called when the user presses the up/down key and wasn't browsing the + history already. + + Args: + text: The preset text. + """ + logging.debug('Preset text: "{}"'.format(text)) + if text: + items = [e for e in self._history if e.startswith(text)] + else: + items = self._history + if not items: + raise HistoryEmptyError + self._tmphist = NeighborList(items) + return self._tmphist.lastitem() + + @pyqtSlot() + def stop(self): + """Stop browsing the history.""" + self._tmphist = None + + def previtem(self): + """Get the previous item in the temp history, or start browsing.""" + if not self.browsing: + raise ValueError("Currently not browsing history") + try: + return self._tmphist.previtem() + except IndexError: + raise HistoryEndReachedError + + def nextitem(self): + """Get the next item in the temp history.""" + if not self.browsing: + raise ValueError("Currently not browsing history") + try: + return self._tmphist.nextitem() + except IndexError: + raise HistoryEndReachedError + + def append(self, text): + """Append a new item to the history. + + Args: + text: The text to append. + """ + if not self._history or text != self._history[-1]: + self._history.append(text) diff --git a/qutebrowser/widgets/statusbar.py b/qutebrowser/widgets/statusbar.py index dc870ab1a..3ac0bca0e 100644 --- a/qutebrowser/widgets/statusbar.py +++ b/qutebrowser/widgets/statusbar.py @@ -17,20 +17,18 @@ """Widgets needed in the qutebrowser statusbar.""" -import logging - from PyQt5.QtCore import pyqtSignal, pyqtSlot, pyqtProperty, Qt from PyQt5.QtWidgets import (QWidget, QLineEdit, QProgressBar, QLabel, QHBoxLayout, QStackedLayout, QSizePolicy, QShortcut) from PyQt5.QtGui import QPainter, QKeySequence, QValidator -import qutebrowser.config.config as config from qutebrowser.config.style import set_register_stylesheet, get_stylesheet import qutebrowser.commands.keys as keys from qutebrowser.utils.url import urlstring -from qutebrowser.utils.usertypes import NeighborList from qutebrowser.commands.parsers import split_cmdline +from qutebrowser.models.cmdhistory import (History, HistoryEmptyError, + HistoryEndReachedError) class StatusBar(QWidget): @@ -212,10 +210,9 @@ class _Command(QLineEdit): """The commandline part of the statusbar. Attributes: - history: The command history, with newer commands at the bottom. + history: The command history object. _statusbar: The statusbar (parent) QWidget. _shortcuts: Defined QShortcuts to prevent GCing. - _tmphist: The temporary history for history browsing as NeighborList. _validator: The current command validator. Signals: @@ -235,8 +232,6 @@ class _Command(QLineEdit): hide_cmd: Emitted when command input can be hidden. """ - # FIXME we should probably use a proper model for the command history. - got_cmd = pyqtSignal(str) got_search = pyqtSignal(str) got_search_rev = pyqtSignal(str) @@ -255,7 +250,6 @@ class _Command(QLineEdit): super().__init__(statusbar) # FIXME self._statusbar = statusbar - self._tmphist = None self.setStyleSheet(""" QLineEdit { border: 0px; @@ -263,16 +257,12 @@ class _Command(QLineEdit): background-color: transparent; } """) + self.history = History() self._validator = _CommandValidator(self) self.setValidator(self._validator) self.returnPressed.connect(self._on_return_pressed) - self.textEdited.connect(self._histbrowse_stop) + self.textEdited.connect(self.history.stop) self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Ignored) - if config.cmd_history.data is None: - self.history = [] - else: - self.history = config.cmd_history.data - self._shortcuts = [] for (key, handler) in [ (Qt.Key_Escape, self.esc_pressed), @@ -287,55 +277,27 @@ class _Command(QLineEdit): sc.activated.connect(handler) self._shortcuts.append(sc) - def _histbrowse_start(self): - """Start browsing to the history. - - Called when the user presses the up/down key and wasn't browsing the - history already. - """ - pre = self.text().strip() - logging.debug('Preset text: "{}"'.format(pre)) - if pre: - items = [e for e in self.history if e.startswith(pre)] - else: - items = self.history - if not items: - raise ValueError("No history found!") - self._tmphist = NeighborList(items) - return self._tmphist.lastitem() - - @pyqtSlot() - def _histbrowse_stop(self): - """Stop browsing the history.""" - self._tmphist = None - @pyqtSlot() def _on_key_up_pressed(self): """Handle Up presses (go back in history).""" - if self._tmphist is None: - try: - item = self._histbrowse_start() - except ValueError: - # no history - return - else: - try: - item = self._tmphist.previtem() - except IndexError: - # at beginning of history - return + try: + if not self.history.browsing: + item = self.history.start(self.text().strip()) + else: + item = self.history.previtem() + except (HistoryEmptyError, HistoryEndReachedError): + return if item: self.set_cmd_text(item) @pyqtSlot() def _on_key_down_pressed(self): """Handle Down presses (go forward in history).""" - if not self._tmphist: + if not self.history.browsing: return try: - item = self._tmphist.nextitem() - except IndexError: - logging.debug("At end of history") + item = self.history.nextitem() + except HistoryEndReachedError: return if item: self.set_cmd_text(item) @@ -354,10 +316,9 @@ class _Command(QLineEdit): '/': self.got_search, '?': self.got_search_rev, } - self._histbrowse_stop() + self.history.stop() text = self.text() - if not self.history or text != self.history[-1]: - self.history.append(text) + self.history.append(text) self.setText('') if text[0] in signals: signals[text[0]].emit(text.lstrip(text[0])) @@ -406,7 +367,7 @@ class _Command(QLineEdit): if e.reason() in [Qt.MouseFocusReason, Qt.TabFocusReason, Qt.BacktabFocusReason, Qt.OtherFocusReason]: self.setText('') - self._histbrowse_stop() + self.history.stop() self.hide_cmd.emit() self.clear_completion_selection.emit() self.hide_completion.emit()