diff --git a/.gitignore b/.gitignore index 7893a33..53bddc5 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ !fish/environments !fish/environments/templates !fish/environments/templates/* +!fish/environments/templates/**/* !atuin !atuin/*.toml diff --git a/fish/environments/templates/cpp/activate.fish b/fish/environments/templates/cpp/activate.fish new file mode 100644 index 0000000..eaefd18 --- /dev/null +++ b/fish/environments/templates/cpp/activate.fish @@ -0,0 +1,51 @@ +# Basic environment with custom prompt +# Environment: {{ENV_NAME}} + +# Check if a previous initialization has occurred +if test -n "$__ENV_INITIALIZED" + echo (set_color yellow)"Environment already initialized"(set_color normal) + return 0 +end + +# Mark as initialized +set -gx __ENV_INITIALIZED "1" +set -gx CURRENT_ENV "{{ENV_NAME}}" + +# Save the original prompt function if it exists +# Only save if we don't already have a backup or if current prompt is not from an environment +if not functions -q __env_orig_prompt + if functions -q fish_prompt + functions -c fish_prompt __env_orig_prompt + else + function __env_orig_prompt + echo -n (whoami)'@'(prompt_hostname)' '(set_color $fish_color_cwd)(prompt_pwd)(set_color normal)'> ' + end + end +else + # If we already have a backup, we're switching environments + # No need to create a new backup +end + +# Define new prompt with environment prefix +function fish_prompt + echo -n (set_color green)'({{ENV_NAME}})'(set_color normal)' ' + __env_orig_prompt +end + +# Add custom environment variables here +# set -gx MY_CUSTOM_VAR "value" + +# Add custom paths here +# fish_add_path /path/to/custom/bin +set script_dir (dirname (status --current-filename)) +fish_add_path $script_dir/bin + +# Custom initialization commands +echo (set_color green)"Activated environment: {{ENV_NAME}}"(set_color normal) + +# Optional: Define custom deactivation function +function __env_custom_deactivate + # Add any cleanup commands here + # unset custom variables, restore paths, etc. + echo (set_color blue)"Custom cleanup for {{ENV_NAME}} completed"(set_color normal) +end \ No newline at end of file diff --git a/fish/environments/templates/cpp/bin/bw b/fish/environments/templates/cpp/bin/bw new file mode 100755 index 0000000..3d46c2a --- /dev/null +++ b/fish/environments/templates/cpp/bin/bw @@ -0,0 +1,226 @@ +#!/bin/bash + +# Watch-build script for automatic rebuilding and restarting +# Monitors source files and runs 'make prod' when changes +# are detected + +set -e + +usage() { + echo "Usage: $(basename "$0") [MAKE_COMMAND...]" + echo "If MAKE_COMMAND is omitted, the default is: 'make clean && make prod'" + echo "Example:" + echo " $(basename "$0") \"make debug\"" + exit 0 +} + +if [[ "$1" == "-h" || "$1" == "--help" ]]; then + usage +fi + +if [[ $# -gt 0 ]]; then + MAKE_CMD="$*" +else + MAKE_CMD="make clean && make prod" +fi + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Process ID of the running program +DEV_PID="" + +cleanup() { + echo -e "\n${YELLOW}Cleaning up...${NC}" + + if [[ -n "$DEV_PID" ]]; then + echo -e "${YELLOW}Stopping running program (PID: $DEV_PID)${NC}" + kill $DEV_PID 2>/dev/null || true + wait $DEV_PID 2>/dev/null || true + fi + + exit 0 +} + +# Trap cleanup on script exit +trap cleanup SIGINT SIGTERM EXIT + +build_and_run() { + echo -e "${BLUE}Building project...${NC}" + echo -e "${BLUE}Running build command: ${YELLOW}$MAKE_CMD${NC}" + + # Stop the currently running program if it exists + if [[ -n "$DEV_PID" ]]; then + echo -e "${YELLOW}Stopping running program (PID: $DEV_PID)${NC}" + kill $DEV_PID 2>/dev/null || true + wait $DEV_PID 2>/dev/null || true + DEV_PID="" + fi + + # Build the project using the provided command and capture output + TMPOUT=$(mktemp) + # Record build start time (epoch seconds) to filter processes started during the build + BUILD_START_TS=$(date +%s) + if bash -c "$MAKE_CMD" 2>&1 | tee "$TMPOUT"; then + echo -e "${GREEN}Build successful! Determining output binary...${NC}" + + # Try to find the output file from the last '-o ' in build output + # Use portable awk parsing to avoid non-GNU grep issues on some systems + OUT_FILE=$(awk '{ + for(i=1;i<=NF;i++){ + if($i=="-o"){ if(i+1<=NF) print $(i+1) } + else if($i ~ /^-o/){ print substr($i,3) } + } + }' "$TMPOUT" | tail -n1) + + # If not found, fall back to newest file in out/ + if [[ -z "$OUT_FILE" ]]; then + OUT_FILE=$(ls -t out/* 2>/dev/null | head -n1 || true) + fi + + # Final fallback to previous default + if [[ -z "$OUT_FILE" ]]; then + OUT_FILE="./out/prod" + fi + + # Normalize path (if relative and doesn't start with ./ or /, prefix ./) + if [[ "$OUT_FILE" != /* && "$OUT_FILE" != ./* ]]; then + OUT_FILE="./$OUT_FILE" + fi + + echo -e "${BLUE}Resolved output binary: ${YELLOW}$OUT_FILE${NC}" + + # If the build itself already started the program, detect an existing PID + EXISTING_PID="" + + # Gather candidate PIDs by matching the basename + if command -v pgrep &> /dev/null; then + CANDIDS=$(pgrep -f "$(basename "$OUT_FILE")" || true) + else + CANDIDS=$(ps aux | grep "$(basename "$OUT_FILE")" | grep -v grep | awk '{print $2}' || true) + fi + + if [[ -n "$CANDIDS" ]]; then + # Resolve the canonical path of the output binary if possible + if command -v realpath &> /dev/null; then + REAL_OUT=$(realpath "$OUT_FILE" 2>/dev/null || true) + else + REAL_OUT=$(readlink -f "$OUT_FILE" 2>/dev/null || true) + fi + + for pid in $CANDIDS; do + # Skip non-numeric entries + if ! [[ "$pid" =~ ^[0-9]+$ ]]; then + continue + fi + + # Check process start time and ignore ones that started before the build + # Use ps lstart to get human-readable start time and convert to epoch + PID_LSTART=$(ps -o lstart= -p "$pid" 2>/dev/null || true) + if [[ -n "$PID_LSTART" ]]; then + PID_EPOCH=$(date -d "$PID_LSTART" +%s 2>/dev/null || true) + if [[ -n "$PID_EPOCH" && "$PID_EPOCH" -lt "$BUILD_START_TS" ]]; then + # process started before build, skip + continue + fi + fi + # Prefer checking /proc//exe for an exact match (Linux) + if [[ -r "/proc/$pid/exe" ]]; then + PID_EXE=$(readlink -f "/proc/$pid/exe" 2>/dev/null || true) + if [[ -n "$PID_EXE" && -n "$REAL_OUT" && "$PID_EXE" = "$REAL_OUT" ]]; then + EXISTING_PID=$pid + break + fi + fi + + # Fallback: check the process cmdline contains the output path or basename + if [[ -r "/proc/$pid/cmdline" ]]; then + CMDLINE=$(tr '\0' ' ' < /proc/$pid/cmdline 2>/dev/null || true) + if [[ -n "$CMDLINE" && ( "$CMDLINE" = *"$OUT_FILE"* || "$CMDLINE" = *"$(basename "$OUT_FILE")"* ) ]]; then + EXISTING_PID=$pid + break + fi + fi + done + fi + + # if [[ -n "$EXISTING_PID" ]]; then + # DEV_PID=$EXISTING_PID + # echo -e "${YELLOW}Program already running (PID: $DEV_PID). Reusing it.${NC}" + # else + if [[ -x "$OUT_FILE" || -f "$OUT_FILE" ]]; then + # Start the program in background + "$OUT_FILE" & + DEV_PID=$! + echo -e "${GREEN}Program started with PID: $DEV_PID${NC}" + else + echo -e "${RED}Resolved output '$OUT_FILE' does not exist or is not executable.${NC}" + DEV_PID="" + fi + # fi + + # clean up temp + rm -f "$TMPOUT" || true + else + echo -e "${RED}build failed${NC}" + DEV_PID="" + rm -f "$TMPOUT" || true + fi +} + +clear_console() { + if command -v tput &> /dev/null; then + tput reset + elif command -v clear &> /dev/null; then + clear + else + printf '\033c' + fi +} + +check_dependencies() { + if ! command -v inotifywait &> /dev/null; then + echo -e "${RED}error:${NC} inotifywait is not installed." + echo -e "Please install ${YELLOW}inotify-tools${NC}" + exit 1 + fi +} + +main() { + clear_console + + check_dependencies + + echo -e "${GREEN}watching on changes...${NC}" + echo -e "monitoring: ${BLUE}src/, lib/, makefile${NC}" + echo "" + + build_and_run + + # Watch for changes + while true; do + inotifywait -e modify,create,delete,move \ + --recursive \ + --quiet \ + src/ lib/ makefile 2>/dev/null \ + || { + echo -e "${RED}error:${NC} Failed to watch directories" + exit 1 + } + + echo -e "\n${BLUE}file change detected, rebuilding${NC}" + + # Small delay to handle rapid successive changes + sleep 0.5 + + clear_console + build_and_run + echo -e "${BLUE}watching for changes${NC}" + done +} + +main diff --git a/fish/environments/templates/cpp/readme.norg b/fish/environments/templates/cpp/readme.norg new file mode 100644 index 0000000..f2fdd19 --- /dev/null +++ b/fish/environments/templates/cpp/readme.norg @@ -0,0 +1,67 @@ +* Exported Fish Environment: {{ENV_NAME}} +This directory contains a self-contained fish environment. + +** Environment-specific functions + This environment is meant to be used with C(++) projects. It provides the + following utilities: + + - `bw` (build-watch): Continuously builds and runs the the project on source + file changes. + +** Files Structure + @code + .fish/ + |-- activate.fish + |-- readme.norg + |-- bin/ + @end + +** Usage + +*** Automatic Activation (Recommended) + The environment will automatically activate when you `cd` into this directory + if your Fish shell is configured with the auto-activation script. + + @code fish + function check_and_source_activate + if test -f (pwd)/.fish/activate.fish + source (pwd)/.fish/activate.fish + elif test -f (pwd)/activate.fish + source (pwd)/activate.fish + end + end + + function cd + builtin cd $argv && check_and_source_activate + end + @end + +*** Manual Activation + To manually activate the environment, run from the project root: + @code bash + source ./.fish/activate.fish + @end + +*** Deactivation + To deactivate the environment, run: + @code bash + env deactivate + @end + + Or simply `cd` to a different directory if using auto-activation. + +** What This Environment Provides + - Prompt showing the environment name + - Environment-specific aliases and functions + - Custom environment variables + - Automatic cleanup when deactivated + +** Requirements + - Fish shell + - `bass` plugin (`fisher install edc/bass`) for compatibility with bash scripts + +** Sharing + This environment is completely self-contained. You can: + - Copy this directory to another machine + - Share it with others + - Version control it with your project (add .fish/ to your repo) \ No newline at end of file