Last active
March 20, 2024 04:26
-
-
Save philiptaron/87b5889efb90d0a35809669d1d3d8816 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
set -e | |
NIX_ID_RE="[a-zA-Z_][0-9A-Za-z_'-]*" | |
LIB_RE="$(sed -z 's/\n/|/g; s/|$/)/; s/^/(/' /tmp/names/lib)" | |
doWork() { | |
f=$1 | |
# Find the top-level `with` statements | |
for top in $(rg '^(in )?with ' $f | sed 's/in with //; s/with //; s/;//;'); do | |
# We ought to have previously found all the names exported from that attrset. | |
names="/tmp/names/$top" | |
if [ ! -e "$names" ]; then | |
echo Cannot find $names | |
exit 1 | |
fi | |
# Have Nix parse the file (no comments) then search through the identifiers for the names | |
# that were being accessed through the top-level `with`. The pipeline does this: | |
# | |
# 1. Remove comments by parsing the nix file | |
# 2. Select out just the identifiers (plus keywords) | |
# 3. Deduplicate them with `sort` and `uniq` | |
# 4. Have `nixfmt` format the expression | |
# 5. Replace the top-level `with` with `let ... inherit expr ... in` | |
replace="$(nix-instantiate --parse $f | \ | |
rg -o "$NIX_ID_RE" | \ | |
rg -xf $names | \ | |
sort | \ | |
uniq | \ | |
perl -0777 -p -e "s/^/let inherit ($top) /; s/\$/; in EOF/" | \ | |
nixfmt | \ | |
sed 's/^EOF//')" | |
perl -i -p -e "s/^with $top*;/$replace/g" $f | |
# Since we replaced the uses at the top level, remove excess with. | |
perl -i -p -e "s/with $top;\s*//g" $f | |
done | |
# Access names from `lib` in just one way. | |
perl -0777 -i -p -e "s/lib\.${LIB_RE}/\$1/g" $f | |
# Combine adjacent `in ... let` scopes. | |
perl -0777 -i -p -e 's/in\s*let\s*/\n /g' $f | |
# Make sure that the file still parses. | |
while ! nix-instantiate --parse $f > /dev/null; do | |
read -p "Press <Return> to re-parse $f " | |
done | |
# Show the changes | |
git diff $f | |
echo "Look through $f above" | |
# Commit it with a standard commit message. | |
read -p "Press <Return> to commit it! " | |
# Make sure that the file still parses. | |
while ! nix-instantiate --parse $f > /dev/null; do | |
read -p "Press <Return> to re-parse $f " | |
done | |
git commit -m "$(printf 'Avoid top-level `with ...;` in %s' $f)" $f | |
} | |
ROOT="." | |
# If called with an argument, it's a either a file or a directory | |
if [ "$#" -ne 0 ]; then | |
if [ -f "$1" ]; then | |
doWork "$1" | |
exit 0 | |
elif [ -d "$1" ]; then | |
ROOT="$1" | |
else | |
echo "$1 is neither file nor directory" | |
exit 1 | |
fi | |
fi | |
# Loop through each file, working on it. | |
for f in $(rg '^(in )?with ' -l --sort=path -tnix "$ROOT"); do | |
doWork $f | |
done |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment