summaryrefslogtreecommitdiff
path: root/Omni/Ide
diff options
context:
space:
mode:
Diffstat (limited to 'Omni/Ide')
-rwxr-xr-xOmni/Ide/MakeTags.py105
-rwxr-xr-xOmni/Ide/ftags.sh21
-rwxr-xr-xOmni/Ide/hoog.sh17
-rwxr-xr-xOmni/Ide/hooks/commit-msg7
-rwxr-xr-xOmni/Ide/hooks/post-applypatch6
-rwxr-xr-xOmni/Ide/hooks/post-checkout20
-rwxr-xr-xOmni/Ide/hooks/post-commit6
-rwxr-xr-xOmni/Ide/hooks/post-merge6
-rwxr-xr-xOmni/Ide/hooks/post-rewrite6
-rwxr-xr-xOmni/Ide/hooks/pre-auto-gc6
-rwxr-xr-xOmni/Ide/hooks/pre-commit21
-rwxr-xr-xOmni/Ide/hooks/pre-push22
-rwxr-xr-xOmni/Ide/hooks/reference-transaction12
-rwxr-xr-xOmni/Ide/ns.sh50
-rwxr-xr-xOmni/Ide/push.sh20
-rwxr-xr-xOmni/Ide/repl.sh84
-rwxr-xr-xOmni/Ide/run.sh6
-rwxr-xr-xOmni/Ide/ship.sh25
-rwxr-xr-xOmni/Ide/tips.sh12
-rwxr-xr-xOmni/Ide/version.sh15
20 files changed, 467 insertions, 0 deletions
diff --git a/Omni/Ide/MakeTags.py b/Omni/Ide/MakeTags.py
new file mode 100755
index 0000000..add07c0
--- /dev/null
+++ b/Omni/Ide/MakeTags.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python
+"""
+Make tags for internal or external code.
+
+This should run fast, and be executable with just Python, meaning it does not
+require a build step.
+"""
+
+# : out maketags
+# : run universal-ctags
+import argparse
+import os
+import pathlib
+import subprocess
+import tarfile
+import zipfile
+
+
+def main() -> None:
+ """Run ctags on internal or external source code.
+
+ Raises:
+ ValueError: if CODEROOT is not set
+ ArgumentError: when explicit paths aren't provided
+ """
+ coderoot = os.environ.get("CODEROOT")
+ if coderoot is None:
+ msg = "CODEROOT not set"
+ raise ValueError(msg)
+ cabsrc = pathlib.Path(coderoot) / "_" / "src"
+ cli = argparse.ArgumentParser()
+ cli.add_argument(
+ "paths",
+ nargs="*",
+ default=".",
+ help="List of paths to run ctags on. Defaults to '.'",
+ )
+ cli.add_argument(
+ "-x",
+ "--external",
+ action="store_true",
+ help=" ".join([
+ "Use this when `paths` is a list of external packages,",
+ f"they will be extracted or linked into {cabsrc}",
+ ]),
+ )
+ args = cli.parse_args()
+ if args.external and args.paths == ".":
+ msg = "requires explicit paths"
+ raise argparse.ArgumentError(argument=args.external, message=msg)
+ if args.external:
+ extract_and_copy(cabsrc, args.paths)
+ ctags(["--recurse=yes"], cwd=cabsrc)
+ else:
+ ctags(["--exclude=*_/*", "--recurse=yes"], cwd=pathlib.Path(coderoot))
+
+
+def strip_nix_hash(path: str) -> str:
+ """Remove the /nix/store/ and hash prefix from a path."""
+ hash_len = 33
+ return path.removeprefix("/nix/store/")[hash_len:]
+
+
+def extract_and_copy(cabsrc: pathlib.Path, paths: list[str]) -> None:
+ """
+ Extract and copy or link sources.
+
+ Loop over `paths`, if the path is an archive, extract it into `cabsrc`. If
+ its a directory, just symlink the directory into `cabsrc`. Either way, we
+ end up with a directory full of source trees for running ctags on.
+ """
+ for path in paths:
+ outpath: pathlib.Path = cabsrc / strip_nix_hash(path)
+ if outpath.exists():
+ continue
+ if path.endswith(".zip"):
+ out = outpath.with_suffix("")
+ if out.exists():
+ continue
+ zipfile.ZipFile(path).extractall(out) # noqa: S202
+ elif path.endswith(".tar.gz"):
+ out = outpath.with_suffix("").with_suffix("")
+ if out.exists():
+ continue
+ with tarfile.open(path) as tarball:
+ tarball.extractall(out) # noqa: S202
+ elif pathlib.Path(path).is_dir():
+ outpath.symlink_to(path)
+
+
+def ctags(args: list[str], cwd: pathlib.Path = pathlib.Path()) -> None:
+ """Call `ctags` with `args` for both emacs and vim."""
+ os.chdir(cwd)
+ excludes = [
+ "--exclude=.mypy_cache",
+ "--exclude=.git",
+ "--exclude=.direnv",
+ "--exclude=.ruff_cache",
+ ]
+ subprocess.check_call(["ctags", *excludes, *args])
+ subprocess.check_call(["ctags", "-e", *excludes, *args])
+
+
+if __name__ == "__main__":
+ main()
diff --git a/Omni/Ide/ftags.sh b/Omni/Ide/ftags.sh
new file mode 100755
index 0000000..b29d994
--- /dev/null
+++ b/Omni/Ide/ftags.sh
@@ -0,0 +1,21 @@
+#!/usr/bin/env bash
+#
+# search tags with fzf
+#
+ set -euo pipefail
+ tags=${CODEROOT:?}/tags
+ tag_search=$(
+ awk 'BEGIN { FS="\t" } !/^!/ {print toupper($4)"\t"$1"\t"$2"\t"$3}' "$tags" \
+ | cut -c1-80 \
+ | fzf-tmux \
+ --nth=1,2 \
+ --preview-window=down,border-none \
+ --bind="pgdn:preview-page-down" \
+ --bind="pgup:preview-page-up" \
+ --preview "rg --pretty --context 2 --fixed-strings --regexp {+2}"
+ )
+ ${EDITOR:-vim} \
+ "$(cut -f3 <<< "$tag_search")" \
+ -c "set nocst" \
+ -c "silent tag $(cut -f2 <<< "$tag_search")"
+##
diff --git a/Omni/Ide/hoog.sh b/Omni/Ide/hoog.sh
new file mode 100755
index 0000000..237eb78
--- /dev/null
+++ b/Omni/Ide/hoog.sh
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+#
+# search hoogle with fzf
+#
+ set -euo pipefail
+ HOOG="hoogle search --count=200"
+ export FZF_DEFAULT_COMMAND="$HOOG $*"
+ result=$(fzf-tmux \
+ --preview-window=down,border-none \
+ --preview "hoogle search --link --info {+2}" \
+ --bind "change:reload:$HOOG {q} || true" \
+ --ansi \
+ | cut -d' ' -f 1,2 \
+ | sed -e 's/ /./g'
+ )
+ hoogle search --info "$result"
+##
diff --git a/Omni/Ide/hooks/commit-msg b/Omni/Ide/hooks/commit-msg
new file mode 100755
index 0000000..e07d1f4
--- /dev/null
+++ b/Omni/Ide/hooks/commit-msg
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+if ! gitlint --ignore-stdin --staged --msg-filename "$1" run-hook; then
+ backup="$CODEROOT"/.git/COMMIT_EDITMSG.backup
+ cp "$CODEROOT"/.git/COMMIT_EDITMSG "$backup"
+ echo "error: gitlint failed, saved your commit msg as $backup"
+ exit 1
+fi
diff --git a/Omni/Ide/hooks/post-applypatch b/Omni/Ide/hooks/post-applypatch
new file mode 100755
index 0000000..5071dc5
--- /dev/null
+++ b/Omni/Ide/hooks/post-applypatch
@@ -0,0 +1,6 @@
+#!/bin/sh
+## START BRANCHLESS CONFIG
+
+git branchless hook post-applypatch "$@"
+
+## END BRANCHLESS CONFIG
diff --git a/Omni/Ide/hooks/post-checkout b/Omni/Ide/hooks/post-checkout
new file mode 100755
index 0000000..85541a2
--- /dev/null
+++ b/Omni/Ide/hooks/post-checkout
@@ -0,0 +1,20 @@
+#!/usr/bin/env bash
+set -e
+function MakeTags {
+ ${CODEROOT:?}/Omni/Ide/MakeTags.py
+}
+old=$1
+new=$2
+# filter out only the changed haskell files
+mapfile -t changed < <(git diff --diff-filter=d --name-only "$old" "$new" -- '*.hs')
+if [[ ! -r tags ]] || [[ ! -r TAGS ]]
+then
+ MakeTags "$CODEROOT"/**/*
+elif [[ ${#changed[@]} -gt 0 ]]
+then
+ MakeTags "${changed[@]}"
+fi
+## START BRANCHLESS CONFIG
+
+git branchless hook post-checkout "$@"
+## END BRANCHLESS CONFIG
diff --git a/Omni/Ide/hooks/post-commit b/Omni/Ide/hooks/post-commit
new file mode 100755
index 0000000..cd1f195
--- /dev/null
+++ b/Omni/Ide/hooks/post-commit
@@ -0,0 +1,6 @@
+#!/bin/sh
+## START BRANCHLESS CONFIG
+
+git branchless hook post-commit "$@"
+
+## END BRANCHLESS CONFIG
diff --git a/Omni/Ide/hooks/post-merge b/Omni/Ide/hooks/post-merge
new file mode 100755
index 0000000..fcfd314
--- /dev/null
+++ b/Omni/Ide/hooks/post-merge
@@ -0,0 +1,6 @@
+#!/usr/bin/env bash
+"${CODEROOT:?}"/Omni/Ide/hooks/post-checkout 'HEAD@{1}' HEAD
+## START BRANCHLESS CONFIG
+
+git branchless hook post-merge "$@"
+## END BRANCHLESS CONFIG
diff --git a/Omni/Ide/hooks/post-rewrite b/Omni/Ide/hooks/post-rewrite
new file mode 100755
index 0000000..8b3237a
--- /dev/null
+++ b/Omni/Ide/hooks/post-rewrite
@@ -0,0 +1,6 @@
+#!/bin/sh
+## START BRANCHLESS CONFIG
+
+git branchless hook post-rewrite "$@"
+
+## END BRANCHLESS CONFIG
diff --git a/Omni/Ide/hooks/pre-auto-gc b/Omni/Ide/hooks/pre-auto-gc
new file mode 100755
index 0000000..c92a844
--- /dev/null
+++ b/Omni/Ide/hooks/pre-auto-gc
@@ -0,0 +1,6 @@
+#!/bin/sh
+## START BRANCHLESS CONFIG
+
+git branchless hook pre-auto-gc "$@"
+
+## END BRANCHLESS CONFIG
diff --git a/Omni/Ide/hooks/pre-commit b/Omni/Ide/hooks/pre-commit
new file mode 100755
index 0000000..06f1716
--- /dev/null
+++ b/Omni/Ide/hooks/pre-commit
@@ -0,0 +1,21 @@
+#!/usr/bin/env bash
+#
+# - prevent frozen code from being checked in
+# - guard against lint errors
+##
+ set -e
+ mapfile -t changed < <(git diff-index --cached --name-only HEAD)
+ for ns in "${changed[@]}"
+ do
+ version=$("${CODEROOT:?}"/Omni/Ide/version.sh "$ns")
+ if [[ $version -eq -1 ]]; then
+ echo "info: version: $ns: deleted"
+ elif [[ $version -lt 1 ]]; then
+ echo "fail: version: $ns: $version"
+ exit 1
+ else
+ echo "info: version: $ns: $version"
+ fi
+ done
+ lint "${changed[@]}"
+##
diff --git a/Omni/Ide/hooks/pre-push b/Omni/Ide/hooks/pre-push
new file mode 100755
index 0000000..00110bd
--- /dev/null
+++ b/Omni/Ide/hooks/pre-push
@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+set -euo pipefail
+remote="$1"
+z40=0000000000000000000000000000000000000000
+IFS=" "
+while read local_ref local_sha remote_ref remote_sha
+do
+ if [ "$local_sha" = $z40 ]
+ then
+ # delete, do nothing
+ continue
+ elif [ "$remote_sha" = $z40 ]
+ then
+ # new branch, test all commits since ci was implemented
+ range="11d95581fb178a5d21e88dfd8030a61886cc2519..$local_sha"
+ else
+ range="$remote_sha..$local_sha"
+ fi
+done
+gitlint --commits "$range" lint
+git test run --command ci "$range"
+git push "$remote" refs/notes/ci --no-verify
diff --git a/Omni/Ide/hooks/reference-transaction b/Omni/Ide/hooks/reference-transaction
new file mode 100755
index 0000000..ea0cce6
--- /dev/null
+++ b/Omni/Ide/hooks/reference-transaction
@@ -0,0 +1,12 @@
+#!/bin/sh
+## START BRANCHLESS CONFIG
+
+# Avoid canceling the reference transaction in the case that `branchless` fails
+# for whatever reason.
+git branchless hook reference-transaction "$@" || (
+echo 'branchless: Failed to process reference transaction!'
+echo 'branchless: Some events (e.g. branch updates) may have been lost.'
+echo 'branchless: This is a bug. Please report it.'
+)
+
+## END BRANCHLESS CONFIG
diff --git a/Omni/Ide/ns.sh b/Omni/Ide/ns.sh
new file mode 100755
index 0000000..a56ed89
--- /dev/null
+++ b/Omni/Ide/ns.sh
@@ -0,0 +1,50 @@
+#!/usr/bin/env bash
+set -euo pipefail
+nss="fd --color=always --exclude=_ -t f . \"${CODEROOT:?}\" | sed \"s,${CODEROOT:?}/*,,g\""
+keybindings=$(cat <<EOF
+repl {}:enter
+repl --bash {}:alt+enter
+edit {} with $EDITOR:tab
+lint -f {}:alt+c
+bild {}:alt+space
+bild --test {}:alt+t
+exec {}:alt+e
+ship {}:ctrl+space
+create new namespace:alt+n
+change preview window:alt+0-6
+resize preview window:ctrl+/
+EOF
+)
+fzf_flags=(
+ --ansi
+ --bind "focus:transform-preview-label:echo {}"
+ --bind "?:change-preview(column -o ' -> ' -s':' -t <<< \"$keybindings\")"
+ --bind "alt-n:execute(touch {q})+reload($nss)"
+ --bind "alt-space:execute(bild {} ; read -p [fin])"
+ --bind "tab:execute($EDITOR {})"
+ --bind "alt-c:execute(lint -f {} ; read -p [fin])"
+ --bind "enter:execute(repl.sh {})"
+ --bind "alt-enter:execute(repl.sh --bash {})"
+ --bind "ctrl-space:execute(ship.sh {} ; read -p [fin])"
+ --bind "alt-t:execute(bild {} ; run.sh {} test ; read -p [fin])"
+ --bind "ctrl-/:change-preview-window(right,88|right,70%|hidden|)"
+ --bind "alt-0:change-preview(bat -p --color=always {})"
+ --bind "alt-1:change-preview(git log --color=always --date=relative --abbrev-commit --pretty=format:'%Cred%h%Creset %s / %an %Creset%C(yellow)%d%Creset%Cgreen(%cr)%Creset' -- {})"
+ --bind "alt-2:change-preview(git log --color=always {})"
+ --bind "alt-3:change-preview(git log --color=always -p {})"
+ --bind "alt-4:change-preview(git blame -c --date=short {})"
+ --bind "alt-5:change-preview(git log --pretty=short {} | git shortlog -nse)"
+ --bind "alt-6:change-preview(git log --pretty=short {} | git shortlog)"
+ --bind "backward-eof:abort"
+ --bind "pgup:preview-page-up"
+ --bind "pgdn:preview-page-down"
+ --header-first
+ --header="? for keybindings"
+ --border=top
+ --border-label="$(lolcat -f <<< "hack a namespace")"
+ --color=label:italic
+ --preview-window="bottom,80%"
+ --preview "bat -p --color=always {}"
+)
+sh -c "$nss" | fzf "${fzf_flags[@]}"
+
diff --git a/Omni/Ide/push.sh b/Omni/Ide/push.sh
new file mode 100755
index 0000000..43dff28
--- /dev/null
+++ b/Omni/Ide/push.sh
@@ -0,0 +1,20 @@
+#!/usr/bin/env bash
+# Eventually convert to haskell, see:
+# - https://github.com/awakesecurity/nix-deploy/blob/master/src/Main.hs
+# - http://www.haskellforall.com/2018/08/nixos-in-production.html
+prefix=${PWD/$CODEROOT}
+if [[ "$prefix" == "" ]]
+then
+ target="$1"
+else
+ target="$prefix.$1"
+fi
+what=$(realpath "${CODEROOT:?}/_/nix/$target")
+# hack: get the domain from the systemd service. there does not seem to be a way
+# to get it from nix-instantiate. (or, maybe i should put this in bild --plan?)
+where=$(rg --only-matching --replace '$2' --regexp '(domainname ")(.*)(")' \
+ "$what/etc/systemd/system/domainname.service")
+nix copy --to ssh://"$USER"@"$where" "$what"
+ssh "$USER"@"$where" sudo "$what"/bin/switch-to-configuration switch
+ssh "$USER"@"$where" sudo nix-env --profile /nix/var/nix/profiles/system --set "$what"
+echo "${GRN}good: push: $target${NC}"
diff --git a/Omni/Ide/repl.sh b/Omni/Ide/repl.sh
new file mode 100755
index 0000000..3b6a536
--- /dev/null
+++ b/Omni/Ide/repl.sh
@@ -0,0 +1,84 @@
+#!/usr/bin/env bash
+###
+### a simple complement to bild which only deals with launching repls
+###
+### > repl [opts] <target..>
+###
+### Starts a repl/shell for one or more targets. (Currently, all targets must
+### have the same extension for this to work.) Repls started with this script
+### should bind to `localhost:$PORT`.
+###
+### Options:
+### --bash start bash instead of the target language repl
+help() {
+ sed -rn 's/^### ?//;T;p' "$0"
+}
+if [[ $# == 0 ]] || [[ "$1" == "-h" ]]; then
+ help
+ exit 1
+fi
+##
+ set -e
+ CMD=
+ if [[ "$1" == "--bash" ]]; then
+ CMD="bash"
+ shift
+ fi
+ targets="${*:?}"
+ json=$(bild --plan "${targets[@]}")
+ mapfile -t langdeps < <(jq --raw-output '.[].langdeps | select(length > 0) | join("\n")' <<< "$json")
+ mapfile -t sysdeps < <(jq --raw-output '.[].sysdeps | select(length > 0) | join("\n")' <<< "$json")
+ mapfile -t rundeps < <(jq --raw-output '.[].rundeps | select(length > 0) | join("\n")' <<< "$json")
+ exts=$(jq --raw-output '.[].namespace.ext' <<< "$json" | sort | uniq)
+ packageSet=$(jq --raw-output '.[].packageSet' <<< "$json")
+ module=$(jq --raw-output '.[].mainModule' <<< "$json")
+ BILD="(import ${CODEROOT:?}/Omni/Bild.nix {})"
+ declare -a flags=(--packages "$BILD.pkgs.pkg-config")
+ for lib in "${sysdeps[@]}"; do
+ flags+=(--packages "$BILD.pkgs.${lib}")
+ done
+ for lib in "${rundeps[@]}"; do
+ flags+=(--packages "$BILD.pkgs.${lib}")
+ done
+ case $exts in
+ C)
+ flags+=(--packages "$BILD.pkgs.gcc")
+ command="bash"
+ ;;
+ Hs)
+ if [ -z ${var+PORT} ]; then
+ echo "warn: repl: ghci does not support binding to a port"
+ fi
+ flags+=(--packages "$BILD.haskell.ghcWith (h: with h; [${langdeps[*]}])")
+ command=${CMD:-"ghci -i${CODEROOT:?} -ghci-script ${CODEROOT:?}/.ghci ${targets[@]}"}
+ ;;
+ Scm)
+ for lib in "${langdeps[@]}"; do
+ flags+=(--packages "$BILD.guile-${lib}")
+ done
+ flags+=(--packages "$BILD.guile")
+ command=${CMD:-"guile -L ${CODEROOT:?} -C ${CODEROOT:?}/_/int --r7rs --listen=${PORT:-37146}"}
+ ;;
+ Lisp)
+ flags+=(--packages "$BILD.$packageSet (p: with p; [asdf swank ${langdeps[*]}])")
+ command=${CMD:-"sbcl --eval '(require :asdf)' --eval '(require :swank)' --eval '(swank:create-server :port ${PORT:-4005})' --load $targets"}
+ ;;
+ Rs)
+ flags+=(--packages "$BILD.pkgs.rustc")
+ command=bash
+ ;;
+ Py)
+ langdeps+=("mypy")
+ flags+=(--packages "$BILD.python.pythonWith (p: with p; [${langdeps[*]}])")
+ PYTHONPATH=$CODEROOT:$PYTHONPATH
+ pycommand="python -i $CODEROOT/Omni/Repl.py $module ${targets[*]}"
+ command=${CMD:-"$pycommand"}
+ ;;
+ *)
+ echo "unsupported targets: ${targets[*]}"
+ exit 1
+ ;;
+ esac
+##
+ nix-shell "${flags[@]}" --command "$command" --show-trace
+##
diff --git a/Omni/Ide/run.sh b/Omni/Ide/run.sh
new file mode 100755
index 0000000..506aa92
--- /dev/null
+++ b/Omni/Ide/run.sh
@@ -0,0 +1,6 @@
+#!/usr/bin/env bash
+set -eu
+target=$1
+shift
+out=$(bild --plan "$target" | jq --raw-output ".\"${target}\".out")
+exec "${CODEROOT:?}/_/bin/$out" "$@"
diff --git a/Omni/Ide/ship.sh b/Omni/Ide/ship.sh
new file mode 100755
index 0000000..8783e9b
--- /dev/null
+++ b/Omni/Ide/ship.sh
@@ -0,0 +1,25 @@
+#!/usr/bin/env bash
+#
+# ship <target>...
+#
+# lint, bild, test, and push one or more targets. if no targets are supplied,
+# ship everything we know how to ship
+##
+ set -eu
+ stuff=("${@}")
+ if [[ ${#stuff[@]} -eq 0 ]]
+ then
+ mapfile -t stuff < <(fd -t l . "$CODEROOT/_/nix/" \
+ | sed "s,$CODEROOT/_/nix/,,g" \
+ | fzf --multi --prompt="ship _/nix/" \
+ --preview="file $CODEROOT/_/nix/{}" \
+ --preview-window=bottom,wrap
+ )
+ fi
+ lint "${stuff[@]}"
+ bild --test "${stuff[@]}"
+ for thing in "${stuff[@]}"
+ do
+ push.sh "$thing"
+ done
+##
diff --git a/Omni/Ide/tips.sh b/Omni/Ide/tips.sh
new file mode 100755
index 0000000..453e464
--- /dev/null
+++ b/Omni/Ide/tips.sh
@@ -0,0 +1,12 @@
+#!/usr/bin/env bash
+echo ""
+echo "omnidev" | figlet | lolcat
+echo ""
+echo " bild compile code"
+echo " repl.sh start a repl"
+echo " deps manage dependencies with niv"
+echo " tips.sh show this message"
+echo " lint auto-lint all changed files"
+echo " push.sh send a namespace to the cloud"
+echo " ship.sh lint, bild, and push one (or all) namespace(s)"
+echo ""
diff --git a/Omni/Ide/version.sh b/Omni/Ide/version.sh
new file mode 100755
index 0000000..60f9c91
--- /dev/null
+++ b/Omni/Ide/version.sh
@@ -0,0 +1,15 @@
+#!/usr/bin/env bash
+#
+# simple implementation of kelvin versioning
+##
+ ns=$1
+ if [[ -e "$1" ]]; then
+ commits=$(git log --oneline --follow "$ns" | wc -l)
+ # gold melts at 1337 kelvin, so we start with this
+ # bc we are forging gold here
+ version=$(bc -l <<< "1337 - $commits")
+ echo "$version"
+ else
+ echo -1 # signal that file doesn't exist
+ fi
+##