Utensil's Zettelkasten-style forest of evergreen notes on math and tech.
utensil.tngl.sh/forest/
1#!/usr/bin/env -S uv run
2# /// script
3# requires-python = ">=3.11,<3.12"
4# dependencies = []
5# ///
6import sys
7import re
8
9# foresterize a tree with LaTeX math
10
11if len(sys.argv) != 2:
12 print("Usage: python3 fize.py <tree>")
13 sys.exit(1)
14
15TREE = f"trees/{sys.argv[1]}.tree"
16
17with open(TREE, 'r+') as file:
18 content = file.read()
19 # Fix enumerate and itemize
20 content = re.sub(r'\\begin\{enumerate\}', r'\\ol{', content)
21 content = re.sub(r'\\end\{enumerate\}', r'}', content)
22 content = re.sub(r'\\begin\{itemize\}', r'\\ul{', content)
23 content = re.sub(r'\\end\{itemize\}', r'}', content)
24 content = re.sub(r'\\item (.*)', r'\\li{\1}', content)
25 content = re.sub(r'\\ii (.*)', r'\\li{\1}', content)
26
27 # Convert some LaTeX commands to forester commands
28 content = re.sub(r'\\emph\{([^}]*)\}', r'\\em{\1}', content)
29
30 # Replace all line breaks to `\r` to handle multiline replacements
31 content.replace('\n', '\r')
32
33 # Replace display math `$$...$$` with `##{...}`
34 content = re.sub(r'\$\$([^$]+)\$\$', r'##{\1}', content)
35
36 # Replace inline math `$...$` with `#{...}`
37 content = re.sub(r'\$([^$]+)\$', r'#{\1}', content)
38
39 # Replace LaTeX block openning with a forester block openning, plus a paragraph openning
40 # Particularly, this introduce a line break after the block openning
41 content = re.sub(r'\\texdef\{([^\}]*)\}\{([^\}]*)\}\{', r'\\refdef{\1}{\2}{\n\n\\p{', content)
42 content = re.sub(r'\\texnote\{([^\}]*)\}\{([^\}]*)\}\{', r'\\refnote{\1}{\2}{\n\n\\p{', content)
43 content = re.sub(r'\\minitex\{', r'{\n\n\\p{', content)
44
45 # Before the line containing \refdef, skip; after the line, replace \r\r with }\r\r\\p{
46 content = content.split('\n')
47 skip = True
48 for i in range(len(content)):
49 if re.search(r'\\(refdef|refnote)', content[i]):
50 skip = False
51 continue
52 if not skip:
53 content[i] = content[i].replace('\r\r', '}\r\r\\p{')
54 content = '\n'.join(content)
55
56 # Replace an ending } with } }, the inserted } is to close the paragraph
57 content = re.sub(r'}\s*$', r'}\r\r}\r', content)
58
59 # Replace \( with #{ -- obsolete
60 # content = re.sub(r'\\\(', r'#\{', content)
61
62 # Replace \) with } -- obsolete
63 # content = re.sub(r'\\\)', r'}', content)
64
65 # Replace \[ with ##{ -- obsolete
66 # content = re.sub(r'\\\[', r'##\{', content)
67
68 # Replace \] with } -- obsolete
69 # content = re.sub(r'\\\]', r'}', content)
70
71 # Replace \texdef with \refdef
72 content = re.sub(r'\\texdef', r'\\refdef', content)
73
74 # Replace the file content
75 file.seek(0)
76 file.write(content)
77 file.truncate()