Track user input history for navigation and recall.
| 794 | |
| 795 | |
| 796 | class InputHistory: |
| 797 | """Track user input history for navigation and recall.""" |
| 798 | |
| 799 | def __init__(self, max_entries: int = 1000) -> None: |
| 800 | self._entries: list[str] = [] |
| 801 | self._max_entries = max_entries |
| 802 | self._cursor: int = -1 |
| 803 | |
| 804 | def add(self, text: str) -> None: |
| 805 | """Add an entry to history.""" |
| 806 | if not text.strip(): |
| 807 | return |
| 808 | # Don't add duplicates of the last entry |
| 809 | if self._entries and self._entries[-1] == text: |
| 810 | return |
| 811 | self._entries.append(text) |
| 812 | if len(self._entries) > self._max_entries: |
| 813 | self._entries.pop(0) |
| 814 | self._cursor = len(self._entries) |
| 815 | |
| 816 | def previous(self) -> str | None: |
| 817 | """Get previous history entry (up arrow).""" |
| 818 | if not self._entries: |
| 819 | return None |
| 820 | self._cursor = max(0, self._cursor - 1) |
| 821 | return self._entries[self._cursor] |
| 822 | |
| 823 | def next(self) -> str | None: |
| 824 | """Get next history entry (down arrow).""" |
| 825 | if not self._entries: |
| 826 | return None |
| 827 | self._cursor = min(len(self._entries), self._cursor + 1) |
| 828 | if self._cursor >= len(self._entries): |
| 829 | return "" # Past the end = empty |
| 830 | return self._entries[self._cursor] |
| 831 | |
| 832 | def search(self, prefix: str) -> list[str]: |
| 833 | """Search history for entries starting with prefix.""" |
| 834 | prefix_lower = prefix.lower() |
| 835 | return [e for e in reversed(self._entries) if e.lower().startswith(prefix_lower)] |
| 836 | |
| 837 | def clear(self) -> None: |
| 838 | """Clear all history.""" |
| 839 | self._entries.clear() |
| 840 | self._cursor = -1 |
| 841 | |
| 842 | @property |
| 843 | def entries(self) -> list[str]: |
| 844 | return list(self._entries) |
| 845 | |
| 846 | @property |
| 847 | def size(self) -> int: |
| 848 | return len(self._entries) |
| 849 | |
| 850 | |
| 851 | # --------------------------------------------------------------------------- |
no outgoing calls