97 lines
2.7 KiB
Python
97 lines
2.7 KiB
Python
"""Search functionality for shell history."""
|
|
|
|
from datetime import datetime
|
|
from typing import Optional
|
|
|
|
from fuzzywuzzy import fuzz, process
|
|
|
|
from shellhist.core import HistoryEntry, HistoryStore
|
|
|
|
|
|
def fuzzy_search(
|
|
store: HistoryStore,
|
|
query: str,
|
|
threshold: int = 70,
|
|
limit: int = 20,
|
|
reverse: bool = False,
|
|
recent: bool = False,
|
|
) -> list[tuple[HistoryEntry, int]]:
|
|
"""Search history with fuzzy matching.
|
|
|
|
Args:
|
|
store: HistoryStore to search.
|
|
query: Search query string.
|
|
threshold: Minimum similarity score (0-100).
|
|
limit: Maximum number of results to return.
|
|
reverse: If True, sort by recency (newest first).
|
|
recent: If True, boost scores for recent commands.
|
|
|
|
Returns:
|
|
List of (HistoryEntry, score) tuples sorted by score.
|
|
"""
|
|
commands = store.get_unique_commands()
|
|
|
|
if not commands:
|
|
return []
|
|
|
|
results = []
|
|
now = datetime.now()
|
|
|
|
for command in commands:
|
|
score = fuzz.token_sort_ratio(query.lower(), command.lower())
|
|
|
|
if score >= threshold:
|
|
entry = _get_first_entry(store, command)
|
|
if entry:
|
|
if recent and entry.timestamp:
|
|
hours_old = (now - entry.timestamp).total_seconds() / 3600
|
|
if hours_old < 24:
|
|
score = min(100, score + 10)
|
|
|
|
results.append((entry, score))
|
|
|
|
results.sort(key=lambda x: x[1], reverse=not reverse)
|
|
|
|
return results[:limit]
|
|
|
|
|
|
def _get_first_entry(store: HistoryStore, command: str) -> Optional[HistoryEntry]:
|
|
"""Get the first entry for a command from the store."""
|
|
for entry in store.entries:
|
|
if entry.command == command:
|
|
return entry
|
|
return None
|
|
|
|
|
|
def rank_by_frequency(
|
|
store: HistoryStore,
|
|
results: list[tuple[HistoryEntry, int]],
|
|
boost_recent: bool = False,
|
|
) -> list[tuple[HistoryEntry, int, int]]:
|
|
"""Rank search results by frequency.
|
|
|
|
Args:
|
|
store: HistoryStore for frequency lookups.
|
|
results: List of (HistoryEntry, score) tuples.
|
|
boost_recent: If True, boost scores for recent commands.
|
|
|
|
Returns:
|
|
List of (HistoryEntry, score, frequency) tuples.
|
|
"""
|
|
ranked = []
|
|
now = datetime.now()
|
|
|
|
for entry, score in results:
|
|
freq = store.get_frequency(entry.command)
|
|
|
|
if boost_recent and entry.timestamp:
|
|
hours_old = (now - entry.timestamp).total_seconds() / 3600
|
|
if hours_old < 24:
|
|
freq = freq * 2
|
|
|
|
ranked.append((entry, score, freq))
|
|
|
|
ranked.sort(key=lambda x: (x[1], x[2]), reverse=True)
|
|
|
|
return ranked
|