search.py 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. import os
  2. import sys
  3. import argparse
  4. import re
  5. def search(query, path='.', include=None, exclude=None, is_regex=False):
  6. """
  7. Search for a string or regex in files.
  8. """
  9. results = []
  10. # Compile regex if needed
  11. if is_regex:
  12. try:
  13. pattern = re.compile(query)
  14. except re.error as e:
  15. print(f"Error: Invalid regex: {e}")
  16. return
  17. for root, dirs, files in os.walk(path):
  18. # Skip some common directories
  19. if any(d in root for d in ['.git', 'node_modules', 'dist', '__pycache__', '.gemini']):
  20. continue
  21. for file in files:
  22. # Filter by extension
  23. if include and not any(file.endswith(ext) for ext in include):
  24. continue
  25. if exclude and any(file.endswith(ext) for ext in exclude):
  26. continue
  27. file_path = os.path.join(root, file)
  28. try:
  29. with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
  30. for i, line in enumerate(f, 1):
  31. found = False
  32. if is_regex:
  33. if pattern.search(line):
  34. found = True
  35. else:
  36. if query in line:
  37. found = True
  38. if found:
  39. results.append({
  40. 'file': file_path,
  41. 'line': i,
  42. 'content': line.strip()
  43. })
  44. except Exception as e:
  45. # print(f"Error reading {file_path}: {e}")
  46. continue
  47. return results
  48. def main():
  49. # Force UTF-8 output for Windows
  50. import io
  51. if sys.stdout.encoding != 'utf-8':
  52. sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
  53. parser = argparse.ArgumentParser(description="Clean and reliable code search tool.")
  54. parser.add_argument("query", help="String or regex to search for")
  55. parser.add_argument("--path", default=".", help="Directory to search in")
  56. parser.add_argument("--include", nargs="+", help="Only include files with these extensions (e.g. .vue .ts)")
  57. parser.add_argument("--exclude", nargs="+", help="Exclude files with these extensions")
  58. parser.add_argument("--regex", action="store_true", help="Treat query as a regular expression")
  59. args = parser.parse_args()
  60. # Standardize extensions to start with dot
  61. include = [ext if ext.startswith('.') else f'.{ext}' for ext in args.include] if args.include else None
  62. exclude = [ext if ext.startswith('.') else f'.{ext}' for ext in args.exclude] if args.exclude else None
  63. results = search(args.query, args.path, include, exclude, args.regex)
  64. if not results:
  65. print("No results found.")
  66. return
  67. # Group results by file
  68. from collections import defaultdict
  69. grouped = defaultdict(list)
  70. for res in results:
  71. grouped[res['file']].append(res)
  72. for file_path, matches in grouped.items():
  73. print(f"\n\033[1;34m{file_path}\033[0m")
  74. for m in matches:
  75. print(f" \033[1;32m{m['line']}:\033[0m {m['content']}")
  76. if __name__ == "__main__":
  77. main()