fix: org tangle — fix END_SRC boundaries in mouse.org/slot.org (prose inside code blocks), replace emacs tangle with Python script that handles all blocks
This commit is contained in:
@@ -1,74 +1,62 @@
|
||||
#!/usr/bin/env python3
|
||||
"""tangle.py — Extract code blocks from .org files into .lisp files.
|
||||
|
||||
Reads all .org files in org/ directory, finds #+BEGIN_SRC lisp :tangle <path>
|
||||
blocks, and writes/concatenates them to the specified target paths.
|
||||
|
||||
Blocks with the same :tangle target are concatenated in file order.
|
||||
|
||||
Usage:
|
||||
python3 scripts/tangle.py # tangle all org/ files
|
||||
python3 scripts/tangle.py org/specific.org # tangle one file
|
||||
|
||||
Target paths are relative to the project root (../target from org/ = project/target).
|
||||
"""Simple org-babel tangle replacement.
|
||||
Extracts #+BEGIN_SRC blocks with :tangle headers and writes target files.
|
||||
"""
|
||||
import re
|
||||
import os
|
||||
import sys
|
||||
from collections import OrderedDict
|
||||
import re, os, sys
|
||||
|
||||
PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
ORG_DIR = os.path.join(PROJECT_ROOT, 'org')
|
||||
ORG_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
def tangle_file(org_path):
|
||||
"""Extract tangle blocks from one .org file."""
|
||||
org_path = os.path.join(ORG_DIR, org_path)
|
||||
with open(org_path) as f:
|
||||
content = f.read()
|
||||
|
||||
# Find all tangle blocks with their targets
|
||||
pattern = r'#\+BEGIN_SRC lisp :tangle ([^\n]+)\n(.*?)\n#\+END_SRC'
|
||||
blocks = re.findall(pattern, content, re.DOTALL)
|
||||
|
||||
if not blocks:
|
||||
return 0
|
||||
|
||||
# Group by target path
|
||||
targets = OrderedDict()
|
||||
for tangle_path, code in blocks:
|
||||
# Resolve tangle path: ../src/x.lisp -> src/x.lisp
|
||||
resolved = tangle_path.replace('../', '')
|
||||
full_path = os.path.join(PROJECT_ROOT, resolved)
|
||||
if full_path not in targets:
|
||||
targets[full_path] = []
|
||||
targets[full_path].append(code.strip())
|
||||
|
||||
for full_path, codes in targets.items():
|
||||
os.makedirs(os.path.dirname(full_path), exist_ok=True)
|
||||
combined = '\n\n'.join(codes) + '\n'
|
||||
with open(full_path, 'w') as f:
|
||||
f.write(combined)
|
||||
print(f" {os.path.relpath(full_path, PROJECT_ROOT)} ({len(codes)} blocks, {sum(len(c) for c in codes)} chars)")
|
||||
|
||||
return len(blocks)
|
||||
|
||||
def main():
|
||||
if len(sys.argv) > 1:
|
||||
org_files = [f for f in sys.argv[1:] if f.endswith('.org')]
|
||||
else:
|
||||
org_files = [os.path.join(ORG_DIR, f) for f in os.listdir(ORG_DIR) if f.endswith('.org')]
|
||||
|
||||
total_blocks = 0
|
||||
for org_file in sorted(org_files):
|
||||
name = os.path.basename(org_file)
|
||||
blocks = tangle_file(org_file)
|
||||
if blocks:
|
||||
print(f"{name}: {blocks} blocks")
|
||||
total_blocks += blocks
|
||||
|
||||
if total_blocks > 0:
|
||||
print(f"\nTotal: {total_blocks} code blocks tangled")
|
||||
else:
|
||||
print("No tangle blocks found.")
|
||||
text = f.read()
|
||||
|
||||
# Find all #+BEGIN_SRC blocks with :tangle
|
||||
pattern = re.compile(
|
||||
r'#\+BEGIN_SRC\s+(\w+)\s+(.*?)\n(.*?)\n#\+END_SRC',
|
||||
re.DOTALL
|
||||
)
|
||||
|
||||
count = 0
|
||||
for match in pattern.finditer(text):
|
||||
lang = match.group(1)
|
||||
header = match.group(2)
|
||||
content = match.group(3)
|
||||
|
||||
# Extract :tangle path
|
||||
tangle_match = re.search(r':tangle\s+(\S+)', header)
|
||||
if not tangle_match:
|
||||
continue
|
||||
tangle_path = tangle_match.group(1)
|
||||
|
||||
# Resolve relative path
|
||||
if tangle_path.startswith('../'):
|
||||
target = os.path.normpath(os.path.join(os.path.dirname(org_path), tangle_path))
|
||||
else:
|
||||
target = os.path.join(ORG_DIR, tangle_path)
|
||||
|
||||
# Ensure directory exists
|
||||
os.makedirs(os.path.dirname(target), exist_ok=True)
|
||||
|
||||
# Don't write :tangle no blocks
|
||||
if tangle_path == 'no':
|
||||
continue
|
||||
|
||||
# Write the content (append if same file already written)
|
||||
content = content.rstrip('\n') + '\n'
|
||||
if os.path.exists(target):
|
||||
with open(target, 'a') as f:
|
||||
f.write('\n' + content)
|
||||
else:
|
||||
with open(target, 'w') as f:
|
||||
f.write(content)
|
||||
print(f" {target} ({len(content)} bytes)")
|
||||
count += 1
|
||||
|
||||
return count
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
for f in sys.argv[1:] or ['org/text-input.org']:
|
||||
print(f"Tangling {f}...")
|
||||
c = tangle_file(f)
|
||||
print(f" {c} code blocks")
|
||||
|
||||
Reference in New Issue
Block a user