Skip to content

Instantly share code, notes, and snippets.

@quells
Created December 8, 2025 00:18
Show Gist options
  • Select an option

  • Save quells/25c178eb9d3934a8b5d17e8b6937b8fa to your computer and use it in GitHub Desktop.

Select an option

Save quells/25c178eb9d3934a8b5d17e8b6937b8fa to your computer and use it in GitHub Desktop.
import json
import os
import sys
from typing import Dict, List, Tuple
def main():
query = ""
if len(sys.argv) > 1:
query = sys.argv[1].lstrip(".")
apps = list_files_with_fuzzy_match(["/Applications", "/Users/kwells/Applications"], query)
print(format_items(apps))
def list_files_with_fuzzy_match(directories: List[str], query: str) -> List[Tuple[str, str]]:
results = []
for directory in directories:
if not os.path.exists(directory):
continue
try:
for entry in os.listdir(directory):
full_path = os.path.join(directory, entry)
if not full_path.endswith(".app"):
continue
score = fuzzy_match_score(entry, query)
if score > 0: # Only include matches
name = entry.split(".")[0]
results.append((name, full_path, score))
except PermissionError:
return []
# Sort by score (descending) and then alphabetically
results.sort(key=lambda x: (-x[2], x[0].lower()))
# Return without the score
return [(name, path) for name, path, score in results]
def fuzzy_match_score(text: str, query: str) -> int:
"""
Calculate fuzzy match score between text and query.
Returns:
Score > 0 if match found, 0 if no match.
Higher scores indicate better matches.
"""
if text.startswith("."):
return 0
if not query:
return 100 # Empty query matches everything
text_lower = text.lower()
query_lower = query.lower()
# Exact match gets highest score
if query_lower == text_lower:
return 1000
# Starts with query gets high score
if text_lower.startswith(query_lower):
return 500
# Check if query is substring
if query_lower in text_lower:
return 300
# Fuzzy match: all characters in order
text_idx = 0
query_idx = 0
consecutive_matches = 0
max_consecutive = 0
while text_idx < len(text_lower) and query_idx < len(query_lower):
if text_lower[text_idx] == query_lower[query_idx]:
query_idx += 1
consecutive_matches += 1
max_consecutive = max(max_consecutive, consecutive_matches)
else:
consecutive_matches = 0
text_idx += 1
# If all query characters found in order
if query_idx == len(query_lower):
# Score based on how close together the matches were
return 100 + max_consecutive * 10
return 0 # No match
def format_items(rows: List[Tuple[str, str]]) -> str:
items = [format_item(row) for row in rows]
return json.dumps({"items": items})
def format_item(row: Tuple[str, str]) -> Dict[str, str]:
(name, loc) = row
return {
"title": name,
"subtitle": loc,
"arg": loc,
"icon": {
"path": f"{loc}/Contents/Resources/AppIcon.icns"
}
}
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment