Utensil's Zettelkasten-style forest of evergreen notes on math and tech. utensil.tngl.sh/forest/
0

Configure Feed

Select the types of activity you want to include in your feed.

forest / port_obj.py
3.3 kB 86 lines
1#!/usr/bin/env -S uv run 2# /// script 3# requires-python = ">=3.11,<3.12" 4# dependencies = ["termcolor"] 5# /// 6 7import re 8from pathlib import Path 9from typing import List, Tuple 10import sys 11from termcolor import colored 12 13def find_method_syntax_in_content(content: str) -> Tuple[List[str], List[str]]: 14 """Find all method definitions and calls in content""" 15 # Match method definitions like [draw/something]{ but not \[ escaped ones 16 def_pattern = r'(?<!\\)\[([a-zA-Z0-9_-]+)/([a-zA-Z0-9_/-]+)\]\s*\{' 17 definitions = re.findall(def_pattern, content) 18 19 # Match method calls like #draw/something or #draw/sb/sth 20 call_pattern = r'#([a-zA-Z0-9_-]+)/([a-zA-Z0-9_/-]+)' 21 calls = re.findall(call_pattern, content) 22 23 return definitions, calls 24 25def replace_method_syntax(content: str) -> str: 26 """Replace method syntax with dashes in content""" 27 # Replace definitions [draw/something]{ -> [draw-something]{ but preserve \[ 28 content = re.sub(r'(?<!\\)\[([a-zA-Z0-9_-]+)/([a-zA-Z0-9_/-]+)\]\s*\{', 29 lambda m: f'[{m.group(1)}-{m.group(2).replace("/", "-")}] {{', 30 content) 31 32 # Replace calls #draw/something -> #draw-something 33 content = re.sub(r'#([a-zA-Z0-9_-]+)/([a-zA-Z0-9_/-]+)', 34 lambda m: f'#{m.group(1)}-{m.group(2).replace("/", "-")}', 35 content) 36 37 return content 38 39def process_file(file_path: Path, dry_run: bool = True) -> None: 40 """Process a single file, showing changes in dry-run mode""" 41 try: 42 with open(file_path, 'r', encoding='utf-8') as f: 43 content = f.read() 44 45 definitions, calls = find_method_syntax_in_content(content) 46 47 if not definitions and not calls: 48 return 49 50 print(colored(f"\nProcessing {file_path}:", 'blue')) 51 print(colored(f"Found {len(definitions)} definitions and {len(calls)} calls", 'cyan')) 52 53 for prefix, method in definitions: 54 old = f'[{prefix}/{method}]' 55 new = f'[{prefix}-{method.replace("/", "-")}]' 56 print(f"Definition: {colored(old, 'red')} -> {colored(new, 'green')}") 57 58 for prefix, method in calls: 59 old = f'#{prefix}/{method}' 60 new = f'#{prefix}-{method.replace("/", "-")}' 61 print(f"Call: {colored(old, 'red')} -> {colored(new, 'green')}") 62 63 if not dry_run: 64 new_content = replace_method_syntax(content) 65 with open(file_path, 'w', encoding='utf-8') as f: 66 f.write(new_content) 67 print(colored("File updated", 'green')) 68 else: 69 print(colored("Dry run - no changes made", 'yellow')) 70 71 except Exception as e: 72 print(colored(f"Error processing {file_path}: {str(e)}", 'red')) 73 74def process_files(dry_run: bool = True) -> None: 75 """Process all matching files in the trees directory""" 76 for file_path in Path('trees').glob('*g-*.tree'): 77 process_file(file_path, dry_run) 78 79def main(): 80 dry_run = '--apply' not in sys.argv 81 print(colored(f"Running in {'dry-run' if dry_run else 'apply'} mode", 82 'magenta' if dry_run else 'green')) 83 process_files(dry_run) 84 85if __name__ == '__main__': 86 main()