Files
dots/.config/fish/functions/dotfiles.fish
2026-06-25 01:15:01 +00:00

801 lines
36 KiB
Fish

# =============================================================================
# functions/dotfiles.fish — Dotfiles Manager
# =============================================================================
#
# This file is auto-loaded by Fish when you first run `dotfiles`. It defines
# the `dotfiles` command and all its subcommands. The design is deliberately
# monolithic (one file) so that the whole system is easy to fork, audit, and
# copy around — yet each subcommand is a separate named function for clarity.
#
# ── How it works ────────────────────────────────────────────────────────────
#
# 1. Template files live in $DOTFILES_DIR (default: ~/.config/fish/dotfiles/).
# Each template is named after the dotfile it manages, e.g.:
# dotfiles/tmux.conf → installed to ~/.tmux.conf
# dotfiles/gitconfig → installed to ~/.gitconfig
#
# 2. When you run `dotfiles init`, templates are copied to $HOME with a
# leading dot prepended. Existing files are backed up.
#
# 3. When you run `dotfiles update`, the template is re-copied (with backup).
#
# 4. `dotfiles sync` also pulls the latest templates from the remote repo
# before re-applying them.
#
# 5. `dotfiles status` compares the installed file against its template using
# a checksum so you can see at a glance what's drifted.
#
# ── Convention for template filenames ──────────────────────────────────────
#
# Template name Target file Typical use
# ──────────────────────────────────────────────────────────────────
# tmux.conf ~/.tmux.conf tmux terminal multiplexer
# gitconfig ~/.gitconfig Git version control
# gitignore ~/.gitignore Global Git ignore rules
# starship.toml ~/.config/starship.toml Starship prompt
# bashrc ~/.bashrc Bash (when Fish is absent)
#
# To add support for a new file, simply drop a template in $DOTFILES_DIR
# and run `dotfiles init <name>`. That's it — no registration needed.
#
# =============================================================================
# ══════════════════════════════════════════════════════════════════════════════
# dotfiles — main dispatcher
# ══════════════════════════════════════════════════════════════════════════════
#
# Usage: dotfiles <command> [arguments ...]
#
# Run `dotfiles help` (or just `dotfiles`) for a full list of subcommands.
# ══════════════════════════════════════════════════════════════════════════════
function dotfiles -d "Manage dotfiles and self-update system"
# ── No argument (or explicit help) → show help ─────────────────────────
set -l cmd $argv[1]
switch "$cmd"
case "" help --help -h
dotfiles_help
case version --version -v
dotfiles_version
case status
dotfiles_status $argv[2..]
case init
# `dotfiles init` → init all
# `dotfiles init foo bar` → init only foo and bar
dotfiles_init $argv[2..]
case update
# Same argument pattern as init.
dotfiles_update $argv[2..]
case edit
if set -q argv[2]
dotfiles_edit $argv[2]
else
echo "Usage: dotfiles edit <name>"
echo "Example: dotfiles edit tmux"
return 1
end
case repo
# `dotfiles repo` → show current URL
# `dotfiles repo <url>` → set URL
dotfiles_repo $argv[2]
case sync
dotfiles_sync
case publish
dotfiles_publish
case '*'
echo "dotfiles: unknown command '$cmd'"
echo "Run 'dotfiles help' for usage."
return 1
end
end
# ══════════════════════════════════════════════════════════════════════════════
# dotfiles_help
# ══════════════════════════════════════════════════════════════════════════════
function dotfiles_help -d "Show the dotfiles help text"
echo "Usage: dotfiles <command> [arguments]"
echo ""
echo "Commands:"
echo " help Show this help message"
echo " version Display version and system info"
echo " status [name ...] Show checksum diff for dotfiles"
echo " init [name ...] Create dotfiles from templates"
echo " update [name ...] Re-apply templates (with backup)"
echo " edit <name> Open ~/.<name> in \$EDITOR"
echo " repo [url] Show or set the remote repo URL"
echo " sync Git-pull and re-apply everything"
echo " publish Push local changes to remote repo"
echo " with a version bump"
echo ""
echo "Available managed files:"
_dotfiles_list_managed | string collect
echo ""
echo "Examples:"
echo " dotfiles init Create ~/.tmux.conf, ~/.gitconfig, …"
echo " dotfiles init gitconfig Create only ~/.gitconfig"
echo " dotfiles status Check every managed file for drift"
echo " dotfiles sync Pull + re-apply everything"
echo " dotfiles publish Bump version, commit, and push"
echo ""
echo "The DOTFILES_REPO_URL environment variable controls where sync"
echo "and publish operate. Set it with: dotfiles repo <url>"
end
# ══════════════════════════════════════════════════════════════════════════════
# dotfiles_version
# ══════════════════════════════════════════════════════════════════════════════
function dotfiles_version -d "Show version information"
echo "dotfiles v$DOTFILES_VERSION"
if test -n "$DOTFILES_REPO_URL"
echo "Repository : $DOTFILES_REPO_URL ($DOTFILES_REPO_BRANCH)"
else
echo "Repository : (none set — run 'dotfiles repo <url>' to enable sync)"
end
echo "Shell : Fish $FISH_VERSION"
echo "Config dir : $__fish_config_dir"
echo "Templates : $DOTFILES_DIR"
echo "Backups : $DOTFILES_BACKUP_DIR"
end
# ══════════════════════════════════════════════════════════════════════════════
# dotfiles_status
# ══════════════════════════════════════════════════════════════════════════════
#
# Compares each installed dotfile against its template using MD5 checksums.
# Shows three states:
# ✓ up to date — template and installed file are identical
# ✗ drifted — installed file differs from the template
# - not installed — template exists but target has not been created
#
# If you give one or more names (e.g. `dotfiles status tmux`) only those
# are checked.
# ══════════════════════════════════════════════════════════════════════════════
function dotfiles_status -d "Show checksum diff for managed dotfiles"
set -l targets (_dotfiles_resolve_targets $argv)
echo "Checking managed dotfiles ..."
echo ""
set -l any_drift 0
for name in $targets
set -l tmpl (_dotfiles_template_path "$name")
set -l dest (_dotfiles_target_path "$name")
if not test -f "$tmpl"
echo " - $name (no template at $tmpl)"
continue
end
if not test -f "$dest"
echo " - $name (not installed — run 'dotfiles init $name')"
continue
end
# Compare checksums
set -l tmpl_md5 (md5sum "$tmpl" | cut -d' ' -f1)
set -l dest_md5 (md5sum "$dest" | cut -d' ' -f1)
if test "$tmpl_md5" = "$dest_md5"
echo "$name (up to date)"
else
echo "$name (drifted — run 'dotfiles update $name')"
set any_drift 1
end
end
echo ""
if test "$any_drift" -eq 0
echo "All managed files are up to date."
else
echo "Some files have drifted. Run 'dotfiles update' to re-apply templates."
end
end
# ══════════════════════════════════════════════════════════════════════════════
# dotfiles_init — create dotfiles from templates
# ══════════════════════════════════════════════════════════════════════════════
#
# Creates ~/.<name> from the template at $DOTFILES_DIR/<name>.
# Existing files are backed up before being overwritten.
# ══════════════════════════════════════════════════════════════════════════════
function dotfiles_init -d "Create dotfiles from templates"
set -l targets (_dotfiles_resolve_targets $argv)
if set -q targets[1]
echo "Initialising dotfiles ..."
else
echo "No managed files found in $DOTFILES_DIR"
echo "Add a template file there and run 'dotfiles init <name>'."
return 0
end
set -l did_anything 0
for name in $targets
set -l tmpl (_dotfiles_template_path "$name")
set -l dest (_dotfiles_target_path "$name")
if not test -f "$tmpl"
echo " skipping $name — no template at $tmpl"
continue
end
# Backup any existing file
if test -f "$dest"
set -l backup "$DOTFILES_BACKUP_DIR/$name"
set -l backup_parent (dirname "$backup")
if not test -d "$backup_parent"
mkdir -p "$backup_parent"
end
command cp "$dest" "$backup"
echo " backed up $dest$backup"
end
# Create parent directory if needed (e.g. ~/.config/starship.toml)
set -l parent (dirname "$dest")
if not test -d "$parent"
mkdir -p "$parent"
end
command cp "$tmpl" "$dest"
echo " created $dest"
set did_anything 1
end
if test "$did_anything" -eq 0
echo "Nothing was created."
end
end
# ══════════════════════════════════════════════════════════════════════════════
# dotfiles_update — re-apply templates (with backup)
# ══════════════════════════════════════════════════════════════════════════════
#
# Identical to `dotfiles init` except it prints "updated" rather than
# "created". This distinction is useful when scripting or checking logs.
# (Technically the behaviour is the same — backup + copy — but the user
# intent is different.)
# ══════════════════════════════════════════════════════════════════════════════
function dotfiles_update -d "Re-apply template to existing dotfile"
set -l targets (_dotfiles_resolve_targets $argv)
if not set -q targets[1]
echo "No managed files found in $DOTFILES_DIR"
return 0
end
for name in $targets
set -l tmpl (_dotfiles_template_path "$name")
set -l dest (_dotfiles_target_path "$name")
if not test -f "$tmpl"
echo " skipping $name — no template at $tmpl"
continue
end
# Backup existing file
if test -f "$dest"
set -l backup "$DOTFILES_BACKUP_DIR/$name"
set -l backup_parent (dirname "$backup")
if not test -d "$backup_parent"
mkdir -p "$backup_parent"
end
command cp "$dest" "$backup"
echo " backed up $dest$backup"
end
set -l parent (dirname "$dest")
if not test -d "$parent"
mkdir -p "$parent"
end
command cp "$tmpl" "$dest"
echo " updated $dest"
end
end
# ══════════════════════════════════════════════════════════════════════════════
# dotfiles_edit — open a dotfile in $EDITOR
# ══════════════════════════════════════════════════════════════════════════════
#
# Opens ~/.<name> for editing. If the file doesn't exist yet, you can
# initialise it from the template first with `dotfiles init <name>`.
# ══════════════════════════════════════════════════════════════════════════════
function dotfiles_edit -d "Open a dotfile for editing"
set -l name $argv[1]
set -l dest (_dotfiles_target_path "$name")
if not test -f "$dest"
echo "File $dest does not exist."
echo "Create it first: dotfiles init $name"
return 1
end
# $EDITOR is set in config.fish; fall back to nano if unset
$EDITOR "$dest"
end
# ══════════════════════════════════════════════════════════════════════════════
# dotfiles_repo — show or set the remote repository URL
# ══════════════════════════════════════════════════════════════════════════════
#
# The URL is stored persistently in a small text file under the backup dir
# so it survives shell restarts. It is also loaded into the environment
# variable DOTFILES_REPO_URL by config.fish if the file exists.
# ══════════════════════════════════════════════════════════════════════════════
function dotfiles_repo -d "Show or set the remote repository URL"
set -l repo_file "$DOTFILES_BACKUP_DIR/.repo_url"
set -l url $argv[1]
if set -q url[1]
# ── Set the URL ────────────────────────────────────────────────────
echo "$url" >"$repo_file"
set -gx DOTFILES_REPO_URL "$url"
echo "Repository URL set to: $url"
else
# ── Show the current URL ──────────────────────────────────────────
if test -f "$repo_file"
set -l stored_url (command cat "$repo_file")
echo "$stored_url"
else if test -n "$DOTFILES_REPO_URL"
echo "$DOTFILES_REPO_URL"
else
echo "(no repository configured)"
echo ""
echo "Run: dotfiles repo https://github.com/yourname/dotfiles"
end
end
end
# Load persisted repo URL into the environment on shell start.
# This runs when the function file is sourced, so it's evaluated once per
# session (before the first `dotfiles` invocation).
set -l repo_file "$DOTFILES_BACKUP_DIR/.repo_url"
if test -f "$repo_file"
set -gx DOTFILES_REPO_URL (command cat "$repo_file")
end
# Ensure there's always a default branch and dotfiles directory, even if
# config.fish hasn't been reloaded after a framework update.
if not set -q DOTFILES_REPO_BRANCH
set -gx DOTFILES_REPO_BRANCH "main"
end
if not set -q DOTFILES_DIR
set -gx DOTFILES_DIR "$__fish_config_dir/dotfiles"
end
# ══════════════════════════════════════════════════════════════════════════════
# dotfiles_sync — full sync: git pull + update all dotfiles
# ══════════════════════════════════════════════════════════════════════════════
#
# This is the "self-update" mechanism. It:
#
# 1. Checks that DOTFILES_REPO_URL is set
# 2. Clones the repo into a temporary directory (or pulls if already cloned)
# 3. Copies the templates from the cloned repo into DOTFILES_DIR
# 4. Re-applies every template to its target location
#
# Sync also copies config.fish and functions/ from upstream, then
# automatically restarts the shell if framework files changed.
# ══════════════════════════════════════════════════════════════════════════════
function dotfiles_sync -d "Pull remote updates and re-apply all dotfiles"
# ── Step 1: Ensure we have a remote URL ────────────────────────────────
if test -z "$DOTFILES_REPO_URL"
echo "No repository URL configured."
echo "Set one with: dotfiles repo https://github.com/yourname/dotfiles"
return 1
end
echo "Syncing from $DOTFILES_REPO_URL ($DOTFILES_REPO_BRANCH) ..."
echo ""
# ── Step 2: Clone/pull into a temporary directory ──────────────────────
set -l tmpdir (mktemp -d)
echo "Cloning into $tmpdir ..."
if command git clone --branch "$DOTFILES_REPO_BRANCH" --depth 1 "$DOTFILES_REPO_URL" "$tmpdir"
echo "Clone successful."
else
echo "Error: failed to clone repository."
command rm -rf "$tmpdir"
return 1
end
# ── Step 3: Copy upstream templates into our local DOTFILES_DIR ────────
set -l upstream_templates "$tmpdir/.config/fish/dotfiles"
if test -d "$upstream_templates"
echo ""
echo "Updating templates from upstream ..."
if not command cp -r "$upstream_templates/." "$DOTFILES_DIR/"
echo "Error: failed to copy templates to $DOTFILES_DIR"
command rm -rf "$tmpdir"
return 1
end
echo " templates synced from upstream"
else
echo "Warning: no dotfiles/ directory found in the upstream repo."
echo "Expected at: $upstream_templates"
end
# ── Step 4: Re-apply all templates ────────────────────────────────────
echo ""
echo "Re-applying dotfiles ..."
dotfiles_update
# ── Step 5: Copy framework files (config.fish, functions/) ─────────────
echo ""
echo "Updating framework files ..."
set -l had_updates 0
if test -f "$tmpdir/.config/fish/config.fish"
command cp "$tmpdir/.config/fish/config.fish" "$__fish_config_dir/config.fish"
echo " config.fish updated"
set had_updates 1
end
if test -d "$tmpdir/.config/fish/functions"
command cp -r "$tmpdir/.config/fish/functions/." "$__fish_config_dir/functions/"
echo " functions/ updated"
set had_updates 1
end
if test -d "$tmpdir/.config/fish/conf.d"
command cp -r "$tmpdir/.config/fish/conf.d/." "$__fish_config_dir/conf.d/"
echo " conf.d/ updated"
set had_updates 1
end
# ── Step 6: Clean up ───────────────────────────────────────────────────
command rm -rf "$tmpdir"
echo ""
echo "Sync complete."
echo ""
# ── Step 7: Restart shell to apply framework changes ───────────────────
if test "$had_updates" -eq 1
if status is-interactive
echo "Restarting shell to apply changes ..."
exec fish
else
echo "Framework files updated. Restart your shell: exec fish"
end
end
end
# ══════════════════════════════════════════════════════════════════════════════
# dotfiles_publish — push local changes to the remote repo
# ══════════════════════════════════════════════════════════════════════════════
#
# This is the counterpart to `dotfiles sync`. Instead of pulling remote
# templates down, it pushes your local config, functions, templates, and
# conf.d/ files back up to the remote repository.
#
# Workflow:
# 1. Clones the remote repo into a temporary directory
# 2. Copies the current local state on top of it:
# config.fish, functions/, dotfiles/, conf.d/
# 3. Prompts for a version number (auto-suggested minor bump, or custom)
# 4. Updates DOTFILES_VERSION in both the local and cloned config.fish
# 5. Commits (with a message like "dotfiles v1.1.0")
# 6. Pushes to the remote
# 7. Cleans up the temporary clone
#
# After publish, run `dotfiles sync` on any other machine to pick up the
# changes.
# ══════════════════════════════════════════════════════════════════════════════
function dotfiles_publish -d "Push local changes to the remote dotfiles repo"
# ── Step 1: Ensure we have a remote URL ────────────────────────────────
if test -z "$DOTFILES_REPO_URL"
echo "No repository URL configured."
echo "Set one with: dotfiles repo https://github.com/yourname/dotfiles"
return 1
end
echo "Publishing to $DOTFILES_REPO_URL ($DOTFILES_REPO_BRANCH) ..."
# ── Step 2: Clone (or init) the repo ─────────────────────────────────────
set -l tmpdir (mktemp -d)
echo "Preparing $tmpdir ..."
if command git clone --branch "$DOTFILES_REPO_BRANCH" "$DOTFILES_REPO_URL" "$tmpdir" 2>/dev/null
echo "Clone successful."
else
# Remote may not exist yet, or it exists but has no commits.
# Initialise a fresh repo locally and wire up the remote.
echo "Remote branch not found — initialising new repository."
command git init "$tmpdir"
command git -C "$tmpdir" remote add origin "$DOTFILES_REPO_URL"
echo "Local repo ready (origin → $DOTFILES_REPO_URL)."
end
# Make sure the expected branch exists (create it if it doesn't).
if not command git -C "$tmpdir" rev-parse --verify "$DOTFILES_REPO_BRANCH" 2>/dev/null
command git -C "$tmpdir" checkout -b "$DOTFILES_REPO_BRANCH"
echo "Created branch '$DOTFILES_REPO_BRANCH'."
else
command git -C "$tmpdir" checkout "$DOTFILES_REPO_BRANCH"
end
# ── Step 3: Copy local state into the clone ───────────────────────────
# These are the files and directories that make up the dotfiles repo.
set -l local_config "$__fish_config_dir/config.fish"
set -l local_funcs "$__fish_config_dir/functions/"
set -l local_tmpl "$DOTFILES_DIR/"
set -l local_confd "$__fish_config_dir/conf.d/"
set -l local_install "$__fish_config_dir/install.fish"
set -l local_readme "$__fish_config_dir/README.md"
set -l repo_config "$tmpdir/.config/fish/config.fish"
set -l repo_funcs "$tmpdir/.config/fish/functions/"
set -l repo_tmpl "$tmpdir/.config/fish/dotfiles/"
set -l repo_confd "$tmpdir/.config/fish/conf.d/"
set -l repo_install "$tmpdir/install.fish"
set -l repo_readme "$tmpdir/README.md"
# Ensure target directories exist
for d in (dirname "$repo_config") "$repo_funcs" "$repo_tmpl" "$repo_confd"
mkdir -p "$d"
end
echo ""
echo "Copying local state into clone ..."
command cp "$local_config" "$repo_config"
echo " config.fish"
command cp -r "$local_funcs." "$repo_funcs"
echo " functions/"
command cp -r "$local_tmpl." "$repo_tmpl"
echo " dotfiles/"
command cp -r "$local_confd." "$repo_confd"
echo " conf.d/"
if test -f "$local_install"
command cp "$local_install" "$repo_install"
echo " install.fish"
else
echo " (no install.fish)"
end
if test -f "$local_readme"
command cp "$local_readme" "$repo_readme"
echo " README.md"
else
echo " (no README.md)"
end
# ── Step 4: Prompt for version ─────────────────────────────────────────
echo ""
set -l new_version (_dotfiles_prompt_version "$DOTFILES_VERSION")
# ── Step 5: Update version in both local and cloned config.fish ───────
# The version line looks like: set -g DOTFILES_VERSION "X.Y.Z"
set -l version_line 'set -g DOTFILES_VERSION "'"$new_version"'"'
# Update local config.fish
sed -i 's/^set -g DOTFILES_VERSION ".*"$/'"$version_line"'/' "$local_config"
echo "Updated local $local_config → v$new_version"
# Update cloned config.fish
sed -i 's/^set -g DOTFILES_VERSION ".*"$/'"$version_line"'/' "$repo_config"
# Set the session variable so subsequent commands in this function
# and future shell sessions see the new version.
set -g DOTFILES_VERSION "$new_version"
# ── Step 6: Commit and push from the clone ─────────────────────────────
echo ""
command git -C "$tmpdir" add -A
# Check if there's anything to commit
if not command git -C "$tmpdir" status --porcelain | grep -q .
echo "Nothing changed — skipping commit and push."
command rm -rf "$tmpdir"
return 0
end
echo "Committing and pushing ..."
command git -C "$tmpdir" commit -m "dotfiles v$new_version"
if command git -C "$tmpdir" push origin "$DOTFILES_REPO_BRANCH"
echo ""
echo "Published v$new_version successfully."
else
echo ""
echo "Warning: push failed. The commit exists locally in $tmpdir."
echo "Check your credentials and remote URL, then push manually:"
echo " cd $tmpdir && git push origin $DOTFILES_REPO_BRANCH"
# Don't clean up on failure so the user can fix and push manually.
return 1
end
# ── Step 7: Clean up ───────────────────────────────────────────────────
command rm -rf "$tmpdir"
echo ""
echo "Done. Run 'dotfiles sync' on other machines to pull v$new_version."
end
# ══════════════════════════════════════════════════════════════════════════════
# _dotfiles_prompt_version — ask user for version, auto-suggest minor bump
# ══════════════════════════════════════════════════════════════════════════════
#
# Parses the current version (X.Y.Z), suggests X.(Y+1).0, and prompts.
# If the user presses Enter without typing, the suggested bump is used.
# If the user types a custom string (e.g. "2.0.0" or "1.5.0-beta"), that
# is used instead.
# ══════════════════════════════════════════════════════════════════════════════
function _dotfiles_prompt_version -d "Prompt for version, defaulting to minor bump"
set -l current $argv[1]
set -l suggested (_dotfiles_bump_minor "$current")
echo "Current version: v$current" >&2
# Use double quotes so $suggested expands in the prompt string.
read -p "echo \"New version (press Enter for v$suggested): \"" -l custom
if test -z "$custom"
echo "$suggested"
else
echo "$custom"
end
end
# ══════════════════════════════════════════════════════════════════════════════
# _dotfiles_bump_minor — given X.Y.Z, return X.(Y+1).0
# ══════════════════════════════════════════════════════════════════════════════
#
# Works with versions like "1.0.0", "2.15.3", etc. Non-numeric parts
# (e.g. "1.0.0-beta") will cause the math to fail, so we fall back to
# appending ".1" as a best-effort.
# ══════════════════════════════════════════════════════════════════════════════
function _dotfiles_bump_minor -d "Increment the minor version component"
set -l v $argv[1]
# Split on dots: "1.2.3" → major=1, minor=2, patch=3
set -l parts (string split "." "$v")
set -l major $parts[1]
set -l minor $parts[2]
set -l patch $parts[3]
# Default to 0 for missing components
set -q minor[1]; or set minor 0
set -q patch[1]; or set patch 0
# Only attempt arithmetic if all three components are plain integers.
# Use string match to check they are digits only.
if string match -rq '^\d+$' "$major"
and string match -rq '^\d+$' "$minor"
and string match -rq '^\d+$' "$patch"
set -l new_minor (math "$minor + 1")
echo "$major.$new_minor.0"
else
# Non-numeric version — just append a bump suffix.
echo "$v.1"
end
end
# ══════════════════════════════════════════════════════════════════════════════
# Private helper functions (prefixed with _dotfiles_)
# ══════════════════════════════════════════════════════════════════════════════
#
# These are not intended to be called directly from the command line.
# ══════════════════════════════════════════════════════════════════════════════
# -----------------------------------------------------------------------------
# _dotfiles_list_managed
#
# Returns a sorted list of template names in $DOTFILES_DIR.
# Used by the help text and by _dotfiles_resolve_targets.
# -----------------------------------------------------------------------------
function _dotfiles_list_managed -d "List available dotfile templates"
if not test -d "$DOTFILES_DIR"
return 1
end
# Recursively list all regular files relative to DOTFILES_DIR.
# This supports subdirectory-based templates, e.g.:
# dotfiles/tmux.conf → tmux.conf
# dotfiles/config/starship.toml → config/starship.toml
command find "$DOTFILES_DIR" -type f -printf '%P\n' | sort
end
# -----------------------------------------------------------------------------
# _dotfiles_resolve_targets
#
# Converts a list of user-supplied names (e.g. "tmux gitconfig") into the
# full list of targets. If no names are given, falls back to ALL managed
# templates. Unknown names are silently skipped with a warning.
#
# This function is used by init, update, and status.
# -----------------------------------------------------------------------------
function _dotfiles_resolve_targets -d "Resolve user-supplied names to template names"
if set -q argv[1]
# User specified specific names — return them as-is.
# Validation happens downstream (_dotfiles_template_path).
printf "%s\n" $argv
else
# No names given — return everything we have templates for.
_dotfiles_list_managed
end
end
# -----------------------------------------------------------------------------
# _dotfiles_template_path
#
# Returns the full filesystem path to the template for a given name.
# Example: _dotfiles_template_path "tmux" → $DOTFILES_DIR/tmux.conf
# -----------------------------------------------------------------------------
function _dotfiles_template_path -d "Get the template path for a dotfile name"
echo "$DOTFILES_DIR/$argv[1]"
end
# -----------------------------------------------------------------------------
# _dotfiles_target_path
#
# Returns the full filesystem path where the dotfile should be installed.
#
# Convention:
# - If the template name contains a "/", it's treated as a relative path
# under $HOME (e.g. "config/starship.toml" → ~/.config/starship.toml).
# - Otherwise, the template name is prefixed with a dot and placed directly
# under $HOME (e.g. "tmux" → ~/.tmux). Note: tmux.conf is the *filename*,
# so "tmux.conf" → ~/.tmux.conf.
#
# This means:
# Template name Target
# tmux.conf ~/.tmux.conf
# gitconfig ~/.gitconfig
# config/starship.toml ~/.config/starship.toml
#
# To add a file in a subdirectory of $HOME, simply create a template with
# a "/" in its name: "vim/vimrc" → ~/.vim/vimrc
# -----------------------------------------------------------------------------
function _dotfiles_target_path -d "Get the target installation path for a dotfile"
set -l name $argv[1]
if string match -q "*/*" "$name"
# Name contains a "/" → treat as relative path under $HOME
echo "$HOME/.$name"
else
# Name without slash → place directly in $HOME, prefixed with dot
echo "$HOME/.$name"
end
end