on cd23ade help sign in
urbit.org/anchors.py
135 lines 4.0 kB history raw
1#!/usr/bin/env python3
3import os
4import sys
5from pathlib import Path
6from typing import List, Tuple
9# inspired by weights.sh
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"""
141. get a list of all .md files
152. 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)
193. accumulate all anchor setting values for all md files
204. write tuple to 'anchors' file, prompt to edit, open in preferred editor
215. once prompt is answered, read back file
226. read edited 'anchors' file, overwrite new setting for each file
23"""
25ANCHOR_KEY = "insert_anchor_links = "
26ANCHOR_FILE = "anchors"
28def nth_occurrence(arr, target_value, wanted_idx):
29 return [idx for idx, val in enumerate(arr) if val == target_value][wanted_idx - 1]
31def 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}'")
36def find_all_mds() -> List[str]:
37 # the `insert_anchor_links` is only applicable on `_index.md` files (i.e. sections)
38 return [str(p) for p in Path('./').rglob('**/_index.md')]
40def setting_from_frontmatter(lines: List[str]) -> str:
41 # 'insert_anchor_links = "none"' -> 'none'
42 for line in lines:
43 if line.startswith(ANCHOR_KEY):
44 # 1. 'insert_anchor_links = "none"\n'
45 # 2. ['insert_anchor_links = ' '"none"\n'][1] => '"none"\n'
46 # 3. "none\n"
47 # 4. "none"
48 return line \
49 .split(ANCHOR_KEY)[1] \
50 .replace('\"', '') \
51 .replace('\n', '')
53 return "none" # default setting if key not present
55def anchors_from_frontmatter(md_files: List[str]) -> List[Tuple[str, str]]:
56 # returns tuple(setting, file)
57 anchor_settings = []
59 for md_file in md_files:
60 with open(md_file, "r") as f:
61 lines = f.readlines()
62 setting = setting_from_frontmatter(lines)
63 anchor_settings.append((setting, md_file))
65 return anchor_settings
67def write_anchor_file(anchor_settings: List[Tuple[str, str]]):
68 with open(ANCHOR_FILE, "w+") as f:
69 for setting, file in anchor_settings:
70 f.write(f"{setting} {file}\n")
73def prompt_editor():
74 editor = os.getenv('EDITOR', 'nano')
75 os.system(f"{editor} {ANCHOR_FILE}")
76 input("Edit the anchor settings and press enter...")
78def read_anchor_file() -> List[Tuple[str, str]]:
79 anchor_settings = []
81 with open(ANCHOR_FILE, "r") as f:
82 for line in f:
83 line = line.replace('\n', '')
84 setting, file = line.split(' ')
85 anchor_settings.append((setting, file))
87 return anchor_settings
90def write_entry(setting: str, file: str):
91 validate_entry(setting, file)
94 # not optimized
95 with open(file, "r+") as f:
96 lines = f.readlines()
97 # lines = list(filter(bool, text.split('\n'))) # split text by lines,
98 fm_end = nth_occurrence(lines, "+++\n", 2) # get index of end of front matter (signaled by second '+++')
100 # remove existing setting if it exists
101 for line in lines[:fm_end]:
102 if line.startswith(ANCHOR_KEY):
103 lines.remove(line)
104 fm_end -= 1 # correct the index for the removal
106 if setting != "none":
107 # by default, there is no need to specify "none"
108 lines.insert(fm_end, ANCHOR_KEY + f'"{setting}"' + '\n') # insert the setting right before the end of front matter
109 f.seek(0) # go to beginning of file
110 f.writelines(lines) # overwrite with new contents
111 f.truncate() # idk why but necessary for bug-free behviour (otherwise it adds excess newlines and stuff)
115def anchors_to_frontmatter(anchor_settings: List[Tuple[str, str]]):
116 for setting, file in anchor_settings:
117 write_entry(setting, file)
119def cleanup():
120 os.remove("anchors")
123def main():
124 files = find_all_mds()
125 print(f'found {len(files)} .md files')
126 write_anchor_file(anchors_from_frontmatter(files))
127 prompt_editor()
128 anchors_to_frontmatter(read_anchor_file())
129 cleanup()
130 print('done')
133if __name__ == '__main__':
134 main()