Add search toggles, path filters, and find/replace functionality

This commit is contained in:
2026-05-26 13:35:38 -04:00
parent 6c282ac77f
commit 8fdcdaf412
5 changed files with 361 additions and 16 deletions
+73
View File
@@ -759,6 +759,67 @@ def _split_query_tokens(raw: str) -> List[str]:
return tokens
def _passes_search_filters(
file_info: dict,
query_terms: List[str],
raw_query: str,
case_sensitive: bool,
whole_word: bool,
regex: bool,
include_paths: Optional[str],
exclude_paths: Optional[str],
) -> bool:
"""Post-filter a candidate by case-sensitive, whole-word, regex, and path filters."""
title = file_info.get("title", "")
content = file_info.get("content", "")
path = file_info.get("path", "")
search_text = f"{title} {content}"
# --- Regex mode ---
if regex and raw_query:
try:
flags = 0 if case_sensitive else re.IGNORECASE
if whole_word:
pattern = re.compile(rf"\b{raw_query}\b", flags)
else:
pattern = re.compile(raw_query, flags)
if not pattern.search(search_text):
return False
except re.error:
return False # invalid regex
return _passes_path_filters(path, include_paths, exclude_paths)
# --- Case-sensitive ---
if case_sensitive and query_terms:
for term in query_terms:
if term not in search_text:
return False
# --- Whole-word ---
if whole_word and query_terms:
for term in query_terms:
pattern = re.compile(rf"\b{re.escape(term)}\b", re.IGNORECASE)
if not pattern.search(search_text):
return False
# --- Path filters (glob-like) ---
return _passes_path_filters(path, include_paths, exclude_paths)
def _passes_path_filters(path: str, include: Optional[str], exclude: Optional[str]) -> bool:
"""Check if a file path passes include/exclude glob patterns."""
import fnmatch
if include:
patterns = [p.strip() for p in include.split(",") if p.strip()]
if patterns and not any(fnmatch.fnmatch(path, p) for p in patterns):
return False
if exclude:
patterns = [p.strip() for p in exclude.split(",") if p.strip()]
if patterns and any(fnmatch.fnmatch(path, p) for p in patterns):
return False
return True
def advanced_search(
query: str,
vault_filter: str = "all",
@@ -766,6 +827,11 @@ def advanced_search(
limit: int = ADVANCED_SEARCH_DEFAULT_LIMIT,
offset: int = 0,
sort_by: str = "relevance",
case_sensitive: bool = False,
whole_word: bool = False,
regex: bool = False,
include_paths: Optional[str] = None,
exclude_paths: Optional[str] = None,
) -> Dict[str, Any]:
"""Advanced full-text search with TF-IDF scoring, facets, and pagination.
@@ -923,6 +989,13 @@ def advanced_search(
score = 1.0
if score > 0:
# --- Post-filters: case-sensitive, whole-word, regex, path filters ---
if not _passes_search_filters(
file_info, query_terms, query,
case_sensitive, whole_word, regex, include_paths, exclude_paths
):
continue
# Build highlighted snippet
content = file_info.get("content", "")
if has_terms: