on master help sign in
Add script `anchors.py` to programatically manage anchor links
commit85316c5aacfdcec107a1aff93feb69eaaf7016b6 authorYaseen <yaseen.nvt@gmail.com> dateThu 21 Jan 2021, 15:05 (6 years ago) parent4166e68
1 file changed+131
anchors.py+131
anchors.pynew file+131
@@ -0,0 +1,131 @@
1+#!/usr/bin/env python3
2+
3+import os
4+import sys
5+from pathlib import Path
6+from typing import List, Tuple
7+
8+
9+# inspired by weights.sh
10+
11+# NOTE: This script does not prevent you from shooting yourself in the foot.
12+# If you manually set a bogus value for insert_anchor_links it will not fix it for you
13+"""
14+1. get a list of all .md files
15+2. for each md file
16+ a. get front matter
17+ b. get value of key `insert_anchor_links`, default to none if not present
18+ c. output a tuple of (setting, file)
19+3. accumulate all anchor setting values for all md files
20+4. write tuple to 'anchors' file, prompt to edit, open in preferred editor
21+5. once prompt is answered, read back file
22+6. read edited 'anchors' file, overwrite new setting for each file
23+"""
24+
25+ANCHOR_KEY = "insert_anchor_links = "
26+ANCHOR_FILE = "anchors"
27+
28+def nth_occurrence(arr, target_value, wanted_idx):
29+ return [idx for idx, val in enumerate(arr) if val == target_value][wanted_idx - 1]
30+
31+def validate_entry(setting: str, file: str):
32+ if not setting in ["left", "right", "none"]:
33+ sys.exit(f"Invalid setting '{setting}' provided for file '{file}'")
34+
35+
36+def find_all_mds() -> List[str]:
37+ return [str(p) for p in Path('./').rglob('*.md')]
38+
39+def setting_from_frontmatter(lines: List[str]) -> str:
40+ # 'insert_anchor_links = "none"' -> 'none'
41+ for line in lines:
42+ if line.startswith(ANCHOR_KEY):
43+ # 1. 'insert_anchor_links = "none"\n'
44+ # 2. ['insert_anchor_links = ' '"none"\n'][1] => '"none"\n'
45+ # 3. "none\n"
46+ # 4. "none"
47+ return line \
48+ .split(ANCHOR_KEY)[1] \
49+ .replace('\"', '') \
50+ .replace('\n', '')
51+
52+ return "none" # default setting if key not present
53+
54+def anchors_from_frontmatter(md_files: List[str]) -> List[Tuple[str, str]]:
55+ # returns tuple(setting, file)
56+ anchor_settings = []
57+
58+ for md_file in md_files:
59+ with open(md_file, "r") as f:
60+ lines = f.readlines()
61+ setting = setting_from_frontmatter(lines)
62+ anchor_settings.append((setting, md_file))
63+
64+ return anchor_settings
65+
66+def write_anchor_file(anchor_settings: List[Tuple[str, str]]):
67+ with open(ANCHOR_FILE, "w+") as f:
68+ for setting, file in anchor_settings:
69+ f.write(f"{setting} {file}\n")
70+
71+
72+def prompt_editor():
73+ editor = os.getenv('EDITOR', 'nano')
74+ os.system(f"{editor} {ANCHOR_FILE}")
75+ input("Edit the anchor settings and press enter...")
76+
77+def read_anchor_file() -> List[Tuple[str, str]]:
78+ anchor_settings = []
79+
80+ with open(ANCHOR_FILE, "r") as f:
81+ for line in f:
82+ line = line.replace('\n', '')
83+ setting, file = line.split(' ')
84+ anchor_settings.append((setting, file))
85+
86+ return anchor_settings
87+
88+
89+def write_entry(setting: str, file: str):
90+ validate_entry(setting, file)
91+
92+ # not optimized
93+ with open(file, "r+") as f:
94+ lines = f.readlines()
95+ # lines = list(filter(bool, text.split('\n'))) # split text by lines,
96+ fm_end = nth_occurrence(lines, "+++\n", 2) # get index of end of front matter (signaled by second '+++')
97+
98+ # remove existing setting if it exists
99+ for line in lines[:fm_end]:
100+ if line.startswith(ANCHOR_KEY):
101+ lines.remove(line)
102+ fm_end -= 1 # correct the index for the removal
103+
104+ lines.insert(fm_end, ANCHOR_KEY + f'"{setting}"' + '\n') # insert the setting right before the end of front matter
105+ f.seek(0) # go to beginning of file
106+ f.writelines(lines) # overwrite with new contents
107+ f.truncate() # idk why but necessary for bug-free behviour
108+
109+
110+
111+def anchors_to_frontmatter(anchor_settings: List[Tuple[str, str]]):
112+ for setting, file in anchor_settings:
113+ write_entry(setting, file)
114+
115+def cleanup():
116+ os.remove("anchors")
117+
118+
119+def main():
120+ files = find_all_mds()
121+ print(f'found {len(files)} .md files')
122+ write_anchor_file(anchors_from_frontmatter(files))
123+ prompt_editor()
124+ anchors_to_frontmatter(read_anchor_file())
125+ cleanup()
126+ print('done')
127+
128+
129+if __name__ == '__main__':
130+ main()
131+