This commit is contained in:
112
src/utils.py
Normal file
112
src/utils.py
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
"""Utility functions for Code Pattern Search CLI."""
|
||||||
|
|
||||||
|
import re
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
|
def validate_regex_pattern(pattern: str) -> tuple[bool, Optional[str]]:
|
||||||
|
"""Validate a regex pattern.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
tuple: (is_valid, error_message)
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
re.compile(pattern)
|
||||||
|
return True, None
|
||||||
|
except re.error as e:
|
||||||
|
return False, str(e)
|
||||||
|
|
||||||
|
|
||||||
|
def sanitize_search_query(query: str) -> str:
|
||||||
|
"""Sanitize a search query for safe usage."""
|
||||||
|
return re.sub(r'[^\w\s\-_]', '', query).strip()
|
||||||
|
|
||||||
|
|
||||||
|
def format_number(num: int) -> str:
|
||||||
|
"""Format a number with appropriate suffixes."""
|
||||||
|
if num >= 1_000_000:
|
||||||
|
return f"{num / 1_000_000:.1f}M"
|
||||||
|
elif num >= 1_000:
|
||||||
|
return f"{num / 1_000:.1f}K"
|
||||||
|
else:
|
||||||
|
return str(num)
|
||||||
|
|
||||||
|
|
||||||
|
def format_duration(seconds: float) -> str:
|
||||||
|
"""Format a duration in human-readable format."""
|
||||||
|
if seconds < 60:
|
||||||
|
return f"{seconds:.1f}s"
|
||||||
|
elif seconds < 3600:
|
||||||
|
return f"{seconds / 60:.1f}m"
|
||||||
|
else:
|
||||||
|
return f"{seconds / 3600:.1f}h"
|
||||||
|
|
||||||
|
|
||||||
|
def get_language_from_extension(extension: str) -> Optional[str]:
|
||||||
|
"""Get programming language from file extension."""
|
||||||
|
extension_map = {
|
||||||
|
".py": "Python",
|
||||||
|
".js": "JavaScript",
|
||||||
|
".ts": "TypeScript",
|
||||||
|
".jsx": "JavaScript React",
|
||||||
|
".tsx": "TypeScript React",
|
||||||
|
".java": "Java",
|
||||||
|
".go": "Go",
|
||||||
|
".rs": "Rust",
|
||||||
|
".cpp": "C++",
|
||||||
|
".c": "C",
|
||||||
|
".cs": "C#",
|
||||||
|
".rb": "Ruby",
|
||||||
|
".php": "PHP",
|
||||||
|
".swift": "Swift",
|
||||||
|
".kt": "Kotlin",
|
||||||
|
".scala": "Scala",
|
||||||
|
".html": "HTML",
|
||||||
|
".css": "CSS",
|
||||||
|
".json": "JSON",
|
||||||
|
".yaml": "YAML",
|
||||||
|
".yml": "YAML",
|
||||||
|
".md": "Markdown",
|
||||||
|
".sh": "Shell",
|
||||||
|
".xml": "XML",
|
||||||
|
".vue": "Vue",
|
||||||
|
".svelte": "Svelte",
|
||||||
|
}
|
||||||
|
return extension_map.get(extension.lower())
|
||||||
|
|
||||||
|
|
||||||
|
def truncate_text(text: str, max_length: int = 100, suffix: str = "...") -> str:
|
||||||
|
"""Truncate text to a maximum length."""
|
||||||
|
if len(text) <= max_length:
|
||||||
|
return text
|
||||||
|
return text[:max_length - len(suffix)] + suffix
|
||||||
|
|
||||||
|
|
||||||
|
def calculate_similarity(str1: str, str2: str) -> float:
|
||||||
|
"""Calculate string similarity using Levenshtein distance."""
|
||||||
|
if str1 == str2:
|
||||||
|
return 1.0
|
||||||
|
|
||||||
|
len1, len2 = len(str1), len(str2)
|
||||||
|
|
||||||
|
if len1 == 0 or len2 == 0:
|
||||||
|
return 0.0
|
||||||
|
|
||||||
|
matrix = [[0] * (len2 + 1) for _ in range(len1 + 1)]
|
||||||
|
|
||||||
|
for i in range(len1 + 1):
|
||||||
|
matrix[i][0] = i
|
||||||
|
for j in range(len2 + 1):
|
||||||
|
matrix[0][j] = j
|
||||||
|
|
||||||
|
for i in range(1, len1 + 1):
|
||||||
|
for j in range(1, len2 + 1):
|
||||||
|
cost = 0 if str1[i - 1] == str2[j - 1] else 1
|
||||||
|
matrix[i][j] = min(
|
||||||
|
matrix[i - 1][j] + 1,
|
||||||
|
matrix[i][j - 1] + 1,
|
||||||
|
matrix[i - 1][j - 1] + cost,
|
||||||
|
)
|
||||||
|
|
||||||
|
max_len = max(len1, len2)
|
||||||
|
return 1.0 - matrix[len1][len2] / max_len
|
||||||
Reference in New Issue
Block a user