commit 10c555735f65babbebfd23ba10a65d2290e4fb98 Author: Taylor Bockman Date: Sun May 31 12:51:44 2026 -0700 Init V2 config diff --git a/README.md b/README.md new file mode 100644 index 0000000..b3a1cd5 --- /dev/null +++ b/README.md @@ -0,0 +1,182 @@ +# ZSH Configuration + +A modular ZSH configuration designed to be portable, easy to maintain, and friendly to both Linux and macOS. + +The main idea is simple: keep the top-level shell startup files small, then split aliases, functions, exports, prompt setup, keybindings, and OS-specific behavior into separate files. + +## Layout + +| File | Purpose | +|---|---| +| `zshrc` | Main interactive shell configuration. Sources the modular config files. | +| `zprofile` | Login shell configuration. Sources `zshrc` where appropriate. | +| `aliases.zsh` | Shell aliases. | +| `functions.zsh` | Utility function definitions. | +| `exports.zsh` | Cross-platform environment variables and PATH setup. | +| `initializations.zsh` | Tool initialization code, such as language managers or runtime setup. | +| `keybinds.zsh` | Custom ZSH keybindings. | +| `ps1.zsh` | Prompt configuration. | +| `macrc` | macOS-specific configuration, especially Homebrew paths and macOS-only tools. | +| `scripts/` | Helper scripts used by this configuration. | + +## Installation + +Clone the repository: + +```sh +git clone git.xchg.sh:angrygoats/zsh-v2.git ~/zsh-v2 +``` + +Back up any existing ZSH files: + +```sh +mv ~/.zshrc ~/.zshrc.backup 2>/dev/null || true +mv ~/.zprofile ~/.zprofile.backup 2>/dev/null || true +``` + +Symlink the main files: + +```sh +ln -s ~/zsh-v2/zshrc ~/.zshrc +ln -s ~/zsh-v2/zprofile ~/.zprofile +``` + +Reload your shell: + +```sh +source ~/.zshrc +``` + +## Private Configuration + +Private or machine-local values should not be committed to this repository. + +The following files are automatically sourced if present: + +```text +$HOME/.bash_private +$HOME/.private +$HOME/.zsh_private +``` + +Use these for secrets, tokens, private exports, work-specific paths, or anything machine-specific that should not live in Git. + +## OS-Specific Configuration + +macOS-specific configuration belongs in: + +```text +$HOME/.macrc +``` + +This is the right place for Homebrew paths, macOS-only tools, GUI application CLI paths, and Apple Silicon versus Intel Homebrew differences. + +Linux-specific configuration can be placed in a Linux-specific file if needed. + +## PATH Management + +This configuration uses ZSH’s `path` array where possible instead of repeatedly prepending strings to `$PATH`. + +At the end of shell initialization, PATH entries should be deduplicated with: + +```zsh +typeset -U path PATH +``` + +This keeps path ordering predictable while avoiding duplicate entries. + +## Git Completion + +Git completion support is installed automatically on first run into: + +```text +$HOME/.zsh +``` + +The setup downloads: + +```text +git-completion.bash +_git +``` + +from the official Git repository. + +If the download fails, manually place both completion files in `$HOME/.zsh` and reload the shell. + +## Syntax Highlighting + +If the installed ZSH version is new enough, `zsh-syntax-highlighting` is automatically cloned into: + +```text +$HOME/.zsh/zsh-syntax-highlighting +``` + +It is sourced last, which is required for `zsh-syntax-highlighting` to work correctly with most ZSH configurations. + +## Rust + +Rust is initialized from: + +```text +$HOME/.cargo/env +``` + +if that file exists. + +This exposes standard Rust tooling such as: + +```text +cargo +rustc +rustfmt +clippy +rust-analyzer +``` + +when installed through `rustup`. + +## Go + +If Go is installed, this configuration sets: + +```zsh +GOPATH=$HOME/.go +``` + +and adds the Go binary directory to `PATH`. + +## macOS Notes + +On macOS, machine-specific Homebrew setup should live in `macrc`, not in the shared cross-platform exports file. + +Common macOS tools configured there include: + +```text +Homebrew +LLVM / clangd / clang-format +OpenSSL +OpenJDK +Qt +Tcl/Tk +Ghostty +Sublime Text CLI +``` + +The Sublime Text CLI symlink should be created once per machine, not on every shell startup: + +```sh +sudo mkdir -p /usr/local/bin +sudo ln -sf "/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl" /usr/local/bin/subl +``` + +## Design Goals + +This configuration aims to be: + +- Modular +- Portable across Linux and macOS +- Safe to version-control +- Friendly to private machine-local overrides +- Predictable about `PATH` ordering +- Minimal enough to debug when something goes wrong diff --git a/aliases.zsh b/aliases.zsh new file mode 100644 index 0000000..d741bec --- /dev/null +++ b/aliases.zsh @@ -0,0 +1,63 @@ +# Alias +if [[ "$OS_TYPE" != "gnu-linux" ]]; then + alias python='python3' +fi + +# rlwrap provides readline wrapping for programs. +# This alias makes the SBCL REPL usable. +alias sbcl='rlwrap sbcl' + +# Useful git aliases +alias gita='git add' +alias gitap='git add -p' +alias gitc='git commit' +alias gitp='git push' +alias gits='git status' +alias gitd='git diff' + +alias k8='kubectl' + +# Good overrides + +alias ls='ls --color=auto' +alias grep='grep --colour=auto' +alias egrep='egrep --colour=auto' +alias fgrep='fgrep --colour=auto' + +# Don't nuke the computer +alias rm='rm -i' + +# Confirm before overwriting +alias cp='cp -i' + +# Default free to sizes in megabytes +alias free='free -m' + +# Better ls - lists more information for each file +alias ll='ls -lh' + +# Less is a better more +alias more=less + +# df in human readable sizes +alias df='df -h' + +# I use Neovim +if [ -x "$(command -v nvim)" ]; then + alias vim=nvim +fi + +# Human readable ls for the current directory +if [[ "$OS_TYPE" != "gnu-linux" ]]; then + # OS X lt + alias lt='du -sh * | sort -h' +else + alias lt='ls --human-readable --size -1 -S --classify' +fi + + +# Count files in the current directory +alias count='find . -type f | wc -l' + +# Generate sha1 hashes on the fly +alias sha1='openssl sha1' diff --git a/exports.zsh b/exports.zsh new file mode 100644 index 0000000..96e24b6 --- /dev/null +++ b/exports.zsh @@ -0,0 +1,76 @@ +#!/bin/zsh + +# ------------------------- +# Editor +# ------------------------- +if command -v vim >/dev/null 2>&1; then + export EDITOR=vim +else + export EDITOR=vi +fi + +# ------------------------- +# C / C++ compiler defaults +# ------------------------- +if command -v clang >/dev/null 2>&1; then + export CC="$(command -v clang)" +elif command -v gcc >/dev/null 2>&1; then + export CC="$(command -v gcc)" +fi + +if command -v clang++ >/dev/null 2>&1; then + export CXX="$(command -v clang++)" +elif command -v g++ >/dev/null 2>&1; then + export CXX="$(command -v g++)" +fi + +# ------------------------- +# History +# ------------------------- +# Note: HISTCONTROL is bash-specific, but harmless. +# ZSH history dedupe should usually be configured with setopt elsewhere. +export HISTCONTROL=ignoredups + +# ------------------------- +# User paths +# ------------------------- +path=("$HOME/bin" $path) +path=("$HOME/.local/bin" $path) + +# ------------------------- +# Go +# ------------------------- +if command -v go >/dev/null 2>&1; then + export GOPATH="$HOME/.go" + path=("$(go env GOPATH)/bin" $path) +fi + +if [[ "$OSTYPE" == "linux-gnu"* ]]; then + # If the Go install guide was followed for Linux, + # /usr/local/go will contain the go binary. + [[ -d "/usr/local/go/bin" ]] && path=("/usr/local/go/bin" $path) +fi + +# ------------------------- +# Ruby / RVM +# ------------------------- +if [[ -d "$HOME/.rvm/bin" ]]; then + path=("$HOME/.rvm/bin" $path) +fi + +# ----------------------------- +# Rust / Cargo - Let rustup win +# ----------------------------- +if [[ -f "$HOME/.cargo/env" ]]; then + . "$HOME/.cargo/env" +fi + +if [[ -d "$HOME/.cargo/bin" ]]; then + path=("$HOME/.cargo/bin" $path) +fi +# ------------------------- +# OpenSSL fallback +# ------------------------- +if command -v openssl >/dev/null 2>&1; then + export OPENSSL_BIN="$(command -v openssl)" +fi diff --git a/functions.zsh b/functions.zsh new file mode 100644 index 0000000..870997a --- /dev/null +++ b/functions.zsh @@ -0,0 +1,12 @@ +#!/usr/zsh + +parse_git_branch() { + git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/(\1) /' +} + +# If you ever needed weather... +weather() { + curl wttr.in +} + + diff --git a/initializations.zsh b/initializations.zsh new file mode 100644 index 0000000..ef96d44 --- /dev/null +++ b/initializations.zsh @@ -0,0 +1,19 @@ +#!/bin/zsh + +if [[ "$OSTYPE" == linux-gnu* ]]; then + export PATH="/usr/local/bin:$PATH" + export PYENV_ROOT="$HOME/.pyenv" + + [[ -d "$PYENV_ROOT/bin" ]] && export PATH="$PYENV_ROOT/bin:$PATH" + + if command -v pyenv >/dev/null 2>&1; then + eval "$(pyenv init -)" + + if pyenv commands | grep -qx "virtualenv-init"; then + eval "$(pyenv virtualenv-init -)" + fi + fi +fi + +# RVM configuration +[[ -s "$HOME/.rvm/scripts/rvm" ]] && . "$HOME/.rvm/scripts/rvm" diff --git a/keybinds.zsh b/keybinds.zsh new file mode 100644 index 0000000..c75fa72 --- /dev/null +++ b/keybinds.zsh @@ -0,0 +1,3 @@ +if command -v setxkbmap >/dev/null 2>&1 && [[ "$XDG_SESSION_TYPE" == "x11" ]]; then + setxkbmap -option caps:escape +fi diff --git a/macrc b/macrc new file mode 100644 index 0000000..2b6c2c8 --- /dev/null +++ b/macrc @@ -0,0 +1,51 @@ +#!/bin/zsh + +# ------------------------- +# macOS-specific setup +# ------------------------- + +# Prefer Apple Silicon Homebrew, fall back to Intel Homebrew. +if [[ -d "/opt/homebrew" ]]; then + export HOMEBREW_PREFIX="/opt/homebrew" +elif [[ -d "/usr/local/Homebrew" || -d "/usr/local/Cellar" ]]; then + export HOMEBREW_PREFIX="/usr/local" +fi + +if [[ -n "$HOMEBREW_PREFIX" ]]; then + [[ -d "$HOMEBREW_PREFIX/bin" ]] && path=("$HOMEBREW_PREFIX/bin" $path) + [[ -d "$HOMEBREW_PREFIX/sbin" ]] && path=("$HOMEBREW_PREFIX/sbin" $path) + + # LLVM / clangd / clang-format + if [[ -d "$HOMEBREW_PREFIX/opt/llvm/bin" ]]; then + path=("$HOMEBREW_PREFIX/opt/llvm/bin" $path) + export LDFLAGS="-L$HOMEBREW_PREFIX/opt/llvm/lib ${LDFLAGS:-}" + export CPPFLAGS="-I$HOMEBREW_PREFIX/opt/llvm/include ${CPPFLAGS:-}" + fi + + # OpenSSL + if [[ -d "$HOMEBREW_PREFIX/opt/openssl@3" ]]; then + export OPENSSL_ROOT_DIR="$HOMEBREW_PREFIX/opt/openssl@3" + export LDFLAGS="-L$OPENSSL_ROOT_DIR/lib ${LDFLAGS:-}" + export CPPFLAGS="-I$OPENSSL_ROOT_DIR/include ${CPPFLAGS:-}" + export PKG_CONFIG_PATH="$OPENSSL_ROOT_DIR/lib/pkgconfig:${PKG_CONFIG_PATH:-}" + elif [[ -d "$HOMEBREW_PREFIX/opt/openssl" ]]; then + export OPENSSL_ROOT_DIR="$HOMEBREW_PREFIX/opt/openssl" + export LDFLAGS="-L$OPENSSL_ROOT_DIR/lib ${LDFLAGS:-}" + export CPPFLAGS="-I$OPENSSL_ROOT_DIR/include ${CPPFLAGS:-}" + export PKG_CONFIG_PATH="$OPENSSL_ROOT_DIR/lib/pkgconfig:${PKG_CONFIG_PATH:-}" + fi + + # OpenJDK + [[ -d "$HOMEBREW_PREFIX/opt/openjdk/bin" ]] && path=("$HOMEBREW_PREFIX/opt/openjdk/bin" $path) + + # Qt + [[ -d "$HOMEBREW_PREFIX/opt/qt/bin" ]] && path=("$HOMEBREW_PREFIX/opt/qt/bin" $path) + + # Tcl/Tk + [[ -d "$HOMEBREW_PREFIX/opt/tcl-tk/bin" ]] && path=("$HOMEBREW_PREFIX/opt/tcl-tk/bin" $path) +fi + +# Ghostty CLI +if [[ -d "/Applications/Ghostty.app/Contents/MacOS" ]]; then + path=("/Applications/Ghostty.app/Contents/MacOS" $path) +fi diff --git a/ps1.zsh b/ps1.zsh new file mode 100644 index 0000000..b38e1ab --- /dev/null +++ b/ps1.zsh @@ -0,0 +1,10 @@ +#!/bin/zsh + +setopt prompt_subst +autoload -Uz vcs_info +precmd () { vcs_info } + +# Format = [] +zstyle ':vcs_info:*' formats ' %F{green}[%b]%f' + +PS1='%B%n@%m%b %F{blue}%2~%f${vcs_info_msg_0_} %B⇨%b ' \ No newline at end of file diff --git a/scripts/cgen.sh b/scripts/cgen.sh new file mode 100755 index 0000000..a10f999 --- /dev/null +++ b/scripts/cgen.sh @@ -0,0 +1,109 @@ +#! /usr/bin/env bash + +name=$1 +cmake_version=$2 +c_standard=$3 + +# Finds the directory of this script +if [[ "$OS_TYPE" == "linux-gnu" ]]; then + script_dir=$(cd "$( dirname "`readlink -f ${BASH_SOURCE[0]}`")" && pwd) +else + script_dir=$(cd "$( dirname "`greadlink -f ${BASH_SOURCE[0]}`")" && pwd) +fi + +if [ -z $name ] +then + echo "Name must be specified" + echo "" + echo "./cgen.sh [cmake version] [C Standard]" + exit -1 +fi + +if [ -z $cmake_version ] +then + echo "No CMake version supplied...defaulting to 3.15" + cmake_version="3.15" +fi + +if [ -z $c_standard ] +then + echo "No C Standard supplied...defaulting to 11" + c_standard="11" +fi + +mkdir $name +mkdir $name/src +mkdir $name/include + +cmake_config=$name/CMakeLists.txt + +touch $cmake_config + +echo "cmake_minimum_required(VERSION $cmake_version)" >> $cmake_config +echo "project($name)" >> $cmake_config +echo "" >> $cmake_config +echo "set (CMAKE_C_STANDARD $c_standard)" >> $cmake_config +echo "" >> $cmake_config +echo "# Output to bin and lib" >> $cmake_config +echo "set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY \${CMAKE_BINARY_DIR}/lib)" >> $cmake_config +echo "set(CMAKE_LIBRARY_OUTPUT_DIRECTORY \${CMAKE_BINARY_DIR}/lib)" >> $cmake_config +echo "set(CMAKE_RUNTIME_OUTPUT_DIRECTORY \${CMAKE_BINARY_DIR}/bin)" >> $cmake_config +echo "" >> $cmake_config +echo "" >> $cmake_config +echo "# The following lines enable compile_commands.json and dump it in the root of the project" >> $cmake_config +echo "# This is needed for clangd and lsp to work" >> $cmake_config +echo "" >> $cmake_config +echo "set(CMAKE_EXPORT_COMPILE_COMMANDS ON)" >> $cmake_config +echo "" >> $cmake_config +echo "if(EXISTS \"\${CMAKE_CURRENT_BINARY_DIR}/compile_commands.json\")" >> $cmake_config +echo " execute_process(COMMAND \${CMAKE_COMMAND} -E copy_if_different" >> $cmake_config +echo " \${CMAKE_CURRENT_BINARY_DIR}/compile_commands.json" >> $cmake_config +echo " \${CMAKE_CURRENT_SOURCE_DIR}/compile_commands.json" >> $cmake_config +echo " )" >> $cmake_config +echo "endif()" >> $cmake_config +echo "#-------- END COMPILE_COMMANDS.JSON SECTION --------#" >> $cmake_config +echo "" >> $cmake_config +echo "" >> $cmake_config +echo "file(GLOB SOURCE \${PROJECT_SOURCE_DIR}/src/*.c)" >> $cmake_config +echo "file(GLOB INCLUDE \${PROJECT_SOURCE_DIR}/include/*.h)" >> $cmake_config +echo "" >> $cmake_config +echo "" >> $cmake_config +echo "include_directories(\${PROJECT_SOURCE_DIR}/include)" >> $cmake_config +echo "" >> $cmake_config +echo "" >> $cmake_config +echo "# Controls debug printing based on the compiled binary mode - check with #ifdef DEBUG_BUILD" >> $cmake_config +echo "IF(CMAKE_BUILD_TYPE MATCHES DEBUG)" >> $cmake_config +echo "message(\"-- Configuring debug build...\")" >> $cmake_config +echo "add_compile_definitions(DEBUG_BUILD)" >> $cmake_config +echo "ENDIF(CMAKE_BUILD_TYPE MATCHES DEBUG)" >> $cmake_config +echo "" >> $cmake_config +echo "add_executable(\${PROJECT_NAME} \${SOURCE} \${INCLUDE})" >> $cmake_config + + +echo "Copying .clang-format..." +cp $script_dir/cgen_etc/clang_format_template $name/.clang-format + +echo "Copying .gitignore..." +cp $script_dir/cgen_etc/gitignore_template $name/.gitignore + +sample_file=$name/src/main.c + +echo "#include " >> $sample_file +echo "" >> $sample_file +echo "" >> $sample_file +echo "int main(int argc, char **argv)" >> $sample_file +echo "{" >> $sample_file +echo " printf(\"Hello, world!\\n\");" >> $sample_file +echo " return 0;" >> $sample_file +echo "}" >> $sample_file +echo "" >> $sample_file + +git --version 2>&1 >/dev/null +GIT_IS_AVAILABLE=$? + +if [ $GIT_IS_AVAILABLE -eq 0 ] +then + git init $name +fi + +echo "Created project $name." diff --git a/scripts/cgen_etc/clang_format_template b/scripts/cgen_etc/clang_format_template new file mode 100644 index 0000000..db8218d --- /dev/null +++ b/scripts/cgen_etc/clang_format_template @@ -0,0 +1,16 @@ +AlignAfterOpenBracket: Align +AlignConsecutiveMacros: 'true' +AlignConsecutiveAssignments: 'true' +AlignConsecutiveDeclarations: 'true' +AlignTrailingComments: 'true' +BreakBeforeBraces: Allman +IndentCaseLabels: 'true' +IndentWidth: '4' +Language: Cpp +PointerAlignment: Right +SortIncludes: 'true' +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: 'true' +SpaceInEmptyParentheses: 'false' +UseTab: Never +ColumnLimit: 80 \ No newline at end of file diff --git a/scripts/cgen_etc/gitignore_template b/scripts/cgen_etc/gitignore_template new file mode 100644 index 0000000..300d9c4 --- /dev/null +++ b/scripts/cgen_etc/gitignore_template @@ -0,0 +1,97 @@ +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +# VSCode + +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Vim + +# Swap +[._]*.s[a-v][a-z] +!*.svg # comment out if you don't need vector files +[._]*.sw[a-p] +[._]s[a-rt-v][a-z] +[._]ss[a-gi-z] +[._]sw[a-p] + +# Session +Session.vim +Sessionx.vim + +# Temporary +.netrwhist +*~ +# Auto-generated tag files +tags +# Persistent undo +[._]*.un~ + +# CMake + +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps diff --git a/zprofile b/zprofile new file mode 100644 index 0000000..6ef54a9 --- /dev/null +++ b/zprofile @@ -0,0 +1,2 @@ +#!/bin/zsh + diff --git a/zshrc b/zshrc new file mode 100644 index 0000000..2d1143b --- /dev/null +++ b/zshrc @@ -0,0 +1,108 @@ +#!/bin/zsh + +if [[ "$OSTYPE" == "linux-gnu" ]]; then + symlink_dir=$(cd "$( dirname "`readlink -f ${(%):-%N}`" )" && pwd) +else + # OS X is not a fan of `readlink -f` + symlink_dir=$(cd "$( dirname "`readlink ${(%):-%N}`" )" && pwd) +fi + +# Store private information (exports, keys, etc) in .bash_private. +if [[ -f $HOME/.bash_private ]]; then + source $HOME/.bash_private +fi + +# Or alternatively .private... +if [[ -f $HOME/.private ]]; then + source $HOME/.private +fi + +# Or alternatively .zsh_private... +if [[ -f $HOME/.zsh_private ]]; then + source $HOME/.zsh_private +fi + +if [ -f $HOME/.inputrc ]; then + source $HOME/.inputrc +fi + +if [[ "$OSTYPE" == "darwin"* ]]; then + if [ -f $HOME/.macrc ]; then + source $HOME/.macrc + fi +fi + +# Set up projects and gopath folder properly for the export in the export section. + +if [[ ! -d $HOME/projects ]]; then + echo "Making $HOME/projects" + mkdir -p $HOME/projects +fi + +if [[ -x go ]]; then + echo "Making $HOME/.go" + mkdir -p $HOME/.go +fi + + +# Install zsh completion in ~/.zsh if it doesn't exist +if [[ ! -d $HOME/.zsh ]]; then + echo "Installing zsh git completion scripts" + mkdir -p $HOME/.zsh + pushd + + cd $HOME/.zsh + + curl -o git-completion.bash https://raw.githubusercontent.com/git/git/master/contrib/completion/git-completion.bash + curl -o _git https://raw.githubusercontent.com/git/git/master/contrib/completion/git-completion.zsh + + popd +fi + +autoload -U is-at-least + +# Install zsh-syntax-highlighting if it doesn't exist +if is-at-least 4.3.11; then + if [[ ! -d $HOME/.zsh/zsh-syntax-highlighting ]]; then + echo "Installing zsh syntax highlighting" + pushd + + cd $HOME/.zsh + git clone https://github.com/zsh-users/zsh-syntax-highlighting.git + + popd + fi +else + echo "ZSH syntax highlighting requires ZSH >= 4.3.11 (current: $ZSH_VERSION)." +fi + +zstyle ':completion:*:*:git:*' script $HOME/.zsh/git-completion.bash +fpath=(~/.zsh $fpath) + +autoload -Uz compinit && compinit + + +# Pull in other configurations +source $symlink_dir/aliases.zsh +source $symlink_dir/functions.zsh +source $symlink_dir/exports.zsh +source $symlink_dir/initializations.zsh +source $symlink_dir/ps1.zsh +source $symlink_dir/keybinds.zsh + +# ZSH syntax highlighting must be sourced last +source $HOME/.zsh/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh + +# Created by `userpath` on 2022-09-05 23:32:41 +export PATH="$PATH:$HOME/.local/bin" + +# ghcup config - Haskell +[ -f "/home/taylor/.ghcup/env" ] && source "/home/taylor/.ghcup/env" # ghcup-env + +# Ocaml +test -r $HOME/.opam/opam-init/init.sh && . $HOME/.opam/opam-init/init.sh > /dev/null 2> /dev/null || true + +# ------------------------- +# Deduplicate PATH +# ------------------------- +typeset -U path PATH