Compare commits
2 commits
d0b52d9c38
...
efdb64216f
| Author | SHA1 | Date | |
|---|---|---|---|
| efdb64216f | |||
| dc3ee6130e |
12 changed files with 801 additions and 51 deletions
198
bin/gc
Executable file
198
bin/gc
Executable file
|
|
@ -0,0 +1,198 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Conventional commit helper
|
||||||
|
# Usage: gc [type] [message] [-s scope] [-b] [-B body]
|
||||||
|
# gc -e [type] (open in editor with template)
|
||||||
|
# gc (interactive mode)
|
||||||
|
|
||||||
|
set -o errexit
|
||||||
|
set -o nounset
|
||||||
|
|
||||||
|
TYPES="feat fix docs style refactor test chore perf ci build"
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
echo "Usage: gc [type] [message] [--scope scope] [-b] [-B body]"
|
||||||
|
echo ' gc -e [type] # open in $EDITOR'
|
||||||
|
echo " gc (interactive mode)"
|
||||||
|
echo ""
|
||||||
|
echo "Options:"
|
||||||
|
echo ' -e, --edit Open commit message in $EDITOR'
|
||||||
|
echo " --scope Scope of the change"
|
||||||
|
echo " -b, --breaking Mark as breaking change (!)"
|
||||||
|
echo " -B, --body Commit body (longer description)"
|
||||||
|
echo ""
|
||||||
|
echo "Git passthrough flags:"
|
||||||
|
echo " -s, --signoff Add Signed-off-by trailer"
|
||||||
|
echo " -a, --all Stage all modified files"
|
||||||
|
echo " -S, --gpg-sign GPG sign the commit"
|
||||||
|
echo " -v, --verbose Show diff in editor"
|
||||||
|
echo " --amend Amend previous commit"
|
||||||
|
echo ""
|
||||||
|
echo "Types: $TYPES"
|
||||||
|
echo ""
|
||||||
|
echo "Examples:"
|
||||||
|
echo " gc feat 'add user auth'"
|
||||||
|
echo " gc fix 'resolve crash' -s api # fix(api): resolve crash"
|
||||||
|
echo " gc feat 'new api' -b # feat!: new api"
|
||||||
|
echo " gc feat 'new api' -s auth -b # feat(auth)!: new api"
|
||||||
|
echo " gc feat 'big change' -B 'Details here'"
|
||||||
|
echo ' gc -e feat # edit feat commit in $EDITOR'
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
editor_mode() {
|
||||||
|
local type="$1"
|
||||||
|
shift
|
||||||
|
local git_args=("$@")
|
||||||
|
local template
|
||||||
|
|
||||||
|
template=$(mktemp)
|
||||||
|
trap "rm -f $template" EXIT
|
||||||
|
|
||||||
|
cat >"$template" <<'EOF'
|
||||||
|
|
||||||
|
# Conventional Commit Format:
|
||||||
|
# <type>(<scope>)!: <description>
|
||||||
|
#
|
||||||
|
# [optional body]
|
||||||
|
#
|
||||||
|
# [optional footer(s)]
|
||||||
|
#
|
||||||
|
# Types: feat fix docs style refactor test chore perf ci build
|
||||||
|
# Add ! before : for breaking changes (e.g., feat!: or feat(api)!:)
|
||||||
|
#
|
||||||
|
# Examples:
|
||||||
|
# feat: add user authentication
|
||||||
|
# fix(api): resolve null pointer exception
|
||||||
|
# feat(auth)!: change token format
|
||||||
|
# docs: update API documentation
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Prepend type if provided
|
||||||
|
if [ -n "$type" ]; then
|
||||||
|
sed -i.bak "1s/^/$type: /" "$template" && rm -f "$template.bak"
|
||||||
|
fi
|
||||||
|
|
||||||
|
git commit -e -t "$template" "${git_args[@]}"
|
||||||
|
}
|
||||||
|
|
||||||
|
build_message() {
|
||||||
|
local type="$1"
|
||||||
|
local scope="$2"
|
||||||
|
local breaking="$3"
|
||||||
|
local msg="$4"
|
||||||
|
local body="$5"
|
||||||
|
|
||||||
|
local commit_msg="$type"
|
||||||
|
|
||||||
|
if [ -n "$scope" ]; then
|
||||||
|
commit_msg+="($scope)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$breaking" = "true" ]; then
|
||||||
|
commit_msg+="!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
commit_msg+=": $msg"
|
||||||
|
|
||||||
|
if [ -n "$body" ]; then
|
||||||
|
commit_msg+=$'\n\n'"$body"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$commit_msg"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Interactive mode if no args
|
||||||
|
if [ $# -eq 0 ]; then
|
||||||
|
echo "Types: $TYPES"
|
||||||
|
printf "Type: "
|
||||||
|
read -r type
|
||||||
|
printf "Scope (optional): "
|
||||||
|
read -r scope
|
||||||
|
printf "Breaking change? [y/N]: "
|
||||||
|
read -r breaking_input
|
||||||
|
printf "Message: "
|
||||||
|
read -r msg
|
||||||
|
printf "Body (optional, enter for none): "
|
||||||
|
read -r body
|
||||||
|
|
||||||
|
breaking="false"
|
||||||
|
if [[ $breaking_input =~ ^[Yy] ]]; then
|
||||||
|
breaking="true"
|
||||||
|
fi
|
||||||
|
|
||||||
|
commit_msg=$(build_message "$type" "$scope" "$breaking" "$msg" "$body")
|
||||||
|
git commit -m "$commit_msg"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Parse arguments
|
||||||
|
type=""
|
||||||
|
msg=""
|
||||||
|
scope=""
|
||||||
|
breaking="false"
|
||||||
|
body=""
|
||||||
|
use_editor="false"
|
||||||
|
git_args=()
|
||||||
|
|
||||||
|
while [ $# -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
-e | --edit)
|
||||||
|
use_editor="true"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--scope)
|
||||||
|
scope="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-b | --breaking)
|
||||||
|
breaking="true"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-B | --body)
|
||||||
|
body="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-h | --help)
|
||||||
|
usage
|
||||||
|
;;
|
||||||
|
# Pass through git commit flags
|
||||||
|
-s | --signoff | -a | --all | -v | --verbose | -n | --no-verify | --amend | --no-edit)
|
||||||
|
git_args+=("$1")
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-S | --gpg-sign)
|
||||||
|
git_args+=("$1")
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
if [ -z "$type" ]; then
|
||||||
|
type="$1"
|
||||||
|
else
|
||||||
|
msg="$1"
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# Editor mode
|
||||||
|
if [ "$use_editor" = "true" ]; then
|
||||||
|
editor_mode "$type" "${git_args[@]}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Validate type
|
||||||
|
if ! echo "$TYPES" | grep -qw "$type"; then
|
||||||
|
echo "Invalid type: $type"
|
||||||
|
echo "Valid types: $TYPES"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$msg" ]; then
|
||||||
|
echo "Message required"
|
||||||
|
usage
|
||||||
|
fi
|
||||||
|
|
||||||
|
commit_msg=$(build_message "$type" "$scope" "$breaking" "$msg" "$body")
|
||||||
|
git commit -m "$commit_msg" "${git_args[@]}"
|
||||||
105
bin/gc-changelog
Executable file
105
bin/gc-changelog
Executable file
|
|
@ -0,0 +1,105 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Generate changelog from conventional commits
|
||||||
|
# Usage: gc-changelog [from_ref] [to_ref]
|
||||||
|
# gc-changelog # all commits
|
||||||
|
# gc-changelog v1.0.0 # from tag to HEAD
|
||||||
|
# gc-changelog v1.0.0 v2.0.0 # between tags
|
||||||
|
|
||||||
|
set -o errexit
|
||||||
|
set -o nounset
|
||||||
|
|
||||||
|
from_ref=${1:-}
|
||||||
|
to_ref=${2:-HEAD}
|
||||||
|
|
||||||
|
if [ -n "$from_ref" ]; then
|
||||||
|
range="$from_ref..$to_ref"
|
||||||
|
else
|
||||||
|
range=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Collect commits by type
|
||||||
|
declare -A titles=(
|
||||||
|
[feat]="Features"
|
||||||
|
[fix]="Bug Fixes"
|
||||||
|
[docs]="Documentation"
|
||||||
|
[style]="Styles"
|
||||||
|
[refactor]="Refactoring"
|
||||||
|
[test]="Tests"
|
||||||
|
[chore]="Chores"
|
||||||
|
[perf]="Performance"
|
||||||
|
[ci]="CI"
|
||||||
|
[build]="Build"
|
||||||
|
)
|
||||||
|
|
||||||
|
declare -A commits
|
||||||
|
declare -a breaking_changes
|
||||||
|
|
||||||
|
for type in feat fix docs style refactor test chore perf ci build; do
|
||||||
|
commits[$type]=""
|
||||||
|
done
|
||||||
|
|
||||||
|
# Parse commits
|
||||||
|
# Full pattern: type(scope)!: description
|
||||||
|
while IFS= read -r line; do
|
||||||
|
[ -z "$line" ] && continue
|
||||||
|
|
||||||
|
msg=$(echo "$line" | cut -d' ' -f2-)
|
||||||
|
|
||||||
|
for type in feat fix docs style refactor test chore perf ci build; do
|
||||||
|
# Match: type, optional (scope), optional !, colon, space
|
||||||
|
if echo "$msg" | grep -qE "^$type(\([^)]+\))?!?: "; then
|
||||||
|
is_breaking=false
|
||||||
|
if echo "$msg" | grep -qE "^$type(\([^)]+\))?!:"; then
|
||||||
|
is_breaking=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract scope and description
|
||||||
|
if echo "$msg" | grep -qE "^$type\([^)]+\)"; then
|
||||||
|
scope=$(echo "$msg" | sed -E "s/^$type\(([^)]+)\)!?: .*/\1/")
|
||||||
|
desc=$(echo "$msg" | sed -E "s/^$type\([^)]+\)!?: //")
|
||||||
|
else
|
||||||
|
scope=""
|
||||||
|
desc=$(echo "$msg" | sed -E "s/^$type!?: //")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Format the entry
|
||||||
|
if [ -n "$scope" ]; then
|
||||||
|
entry="- **$scope**: $desc"
|
||||||
|
else
|
||||||
|
entry="- $desc"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$is_breaking" = true ]; then
|
||||||
|
breaking_changes+=("$entry")
|
||||||
|
fi
|
||||||
|
|
||||||
|
commits[$type]+="$entry"$'\n'
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
done < <(git log --oneline $range)
|
||||||
|
|
||||||
|
# Output changelog
|
||||||
|
echo "# Changelog"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Breaking changes first
|
||||||
|
if [ ${#breaking_changes[@]} -gt 0 ]; then
|
||||||
|
echo "## BREAKING CHANGES"
|
||||||
|
echo ""
|
||||||
|
for change in "${breaking_changes[@]}"; do
|
||||||
|
echo "$change"
|
||||||
|
done
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Then by type
|
||||||
|
for type in feat fix perf refactor docs style test chore ci build; do
|
||||||
|
if [ -n "${commits[$type]}" ]; then
|
||||||
|
echo "## ${titles[$type]}"
|
||||||
|
echo ""
|
||||||
|
echo -n "${commits[$type]}"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
done
|
||||||
41
bin/gc-check
Executable file
41
bin/gc-check
Executable file
|
|
@ -0,0 +1,41 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Validate conventional commit messages in git history
|
||||||
|
# Usage: gc-check [number_of_commits]
|
||||||
|
|
||||||
|
set -o errexit
|
||||||
|
set -o nounset
|
||||||
|
|
||||||
|
# Full conventional commit regex:
|
||||||
|
# type(optional-scope)optional-!: description
|
||||||
|
COMMIT_REGEX='^(feat|fix|docs|style|refactor|test|chore|perf|ci|build)(\([^)]+\))?!?: .+'
|
||||||
|
|
||||||
|
count=${1:-10}
|
||||||
|
errors=0
|
||||||
|
|
||||||
|
echo "Checking last $count commits..."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
while IFS= read -r line; do
|
||||||
|
hash=$(echo "$line" | cut -d' ' -f1)
|
||||||
|
msg=$(echo "$line" | cut -d' ' -f2-)
|
||||||
|
|
||||||
|
if echo "$msg" | grep -qE "$COMMIT_REGEX"; then
|
||||||
|
if echo "$msg" | grep -qE '^[^:]+!:'; then
|
||||||
|
echo "✓ $hash $msg (BREAKING)"
|
||||||
|
else
|
||||||
|
echo "✓ $hash $msg"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "✗ $hash $msg"
|
||||||
|
errors=$((errors + 1))
|
||||||
|
fi
|
||||||
|
done < <(git log --oneline -n "$count")
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
if [ $errors -gt 0 ]; then
|
||||||
|
echo "$errors invalid commit(s) found"
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo "All commits valid"
|
||||||
|
fi
|
||||||
|
|
@ -40,7 +40,7 @@ elif [ "$1" == "off" ]; then
|
||||||
done <"$BACKUP_FILE"
|
done <"$BACKUP_FILE"
|
||||||
|
|
||||||
# Restore original wallpaper
|
# Restore original wallpaper
|
||||||
osascript -e 'tell application "System Events" to set picture of every desktop to "/Users/rayandrew/Pictures/Wallpapers/bluering.png"' >/dev/null 2>&1
|
osascript -e 'tell application "System Events" to set picture of every desktop to "/Users/rayandrew/Pictures/Wallpapers/420322.jpg"' >/dev/null 2>&1
|
||||||
|
|
||||||
rm "$BACKUP_FILE"
|
rm "$BACKUP_FILE"
|
||||||
aerospace reload-config
|
aerospace reload-config
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,8 @@ else
|
||||||
echo "body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; padding: 30px 20px; max-width: 800px; margin: 0 auto; background: var(--bg); color: var(--text); transition: all 0.2s ease; line-height: 1.5; }"
|
echo "body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; padding: 30px 20px; max-width: 800px; margin: 0 auto; background: var(--bg); color: var(--text); transition: all 0.2s ease; line-height: 1.5; }"
|
||||||
echo "pre { white-space: pre-wrap; word-wrap: break-word; }"
|
echo "pre { white-space: pre-wrap; word-wrap: break-word; }"
|
||||||
echo ".headers { background: var(--header-bg); padding: 20px 25px; margin-bottom: 25px; border-radius: 12px; border: 1px solid var(--border); box-shadow: 0 2px 8px var(--shadow); }"
|
echo ".headers { background: var(--header-bg); padding: 20px 25px; margin-bottom: 25px; border-radius: 12px; border: 1px solid var(--border); box-shadow: 0 2px 8px var(--shadow); }"
|
||||||
echo ".headers p { margin: 8px 0; display: grid; grid-template-columns: 70px 1fr; gap: 8px; align-items: baseline; }"
|
echo ".headers p { margin: 8px 0; display: grid; grid-template-columns: 80px 1fr; gap: 8px; align-items: baseline; }"
|
||||||
|
echo ".headers strong { white-space: nowrap; }"
|
||||||
echo ".headers strong { color: var(--muted); font-weight: 500; }"
|
echo ".headers strong { color: var(--muted); font-weight: 500; }"
|
||||||
echo ".headers .addr-list { display: flex; flex-wrap: wrap; gap: 6px; }"
|
echo ".headers .addr-list { display: flex; flex-wrap: wrap; gap: 6px; }"
|
||||||
echo ".headers .addr { padding: 4px 10px; background: var(--bg); border-radius: 6px; font-size: 0.95em; }"
|
echo ".headers .addr { padding: 4px 10px; background: var(--bg); border-radius: 6px; font-size: 0.95em; }"
|
||||||
|
|
@ -53,7 +54,10 @@ else
|
||||||
echo "a { color: var(--link); text-decoration: none; } a:hover { text-decoration: underline; }"
|
echo "a { color: var(--link); text-decoration: none; } a:hover { text-decoration: underline; }"
|
||||||
echo ".toggle { position: fixed; top: 20px; right: 20px; width: 44px; height: 44px; border: 1px solid var(--border); border-radius: 50%; cursor: pointer; background: var(--header-bg); color: var(--text); font-size: 20px; transition: all 0.2s; box-shadow: 0 2px 8px var(--shadow); }"
|
echo ".toggle { position: fixed; top: 20px; right: 20px; width: 44px; height: 44px; border: 1px solid var(--border); border-radius: 50%; cursor: pointer; background: var(--header-bg); color: var(--text); font-size: 20px; transition: all 0.2s; box-shadow: 0 2px 8px var(--shadow); }"
|
||||||
echo ".toggle:hover { transform: scale(1.05); box-shadow: 0 4px 12px var(--shadow); }"
|
echo ".toggle:hover { transform: scale(1.05); box-shadow: 0 4px 12px var(--shadow); }"
|
||||||
echo ".sun { display: none; } .dark .sun { display: inline; } .dark .moon { display: none; }"
|
echo ".toggle .icon { display: none; }"
|
||||||
|
echo ".toggle.auto .icon-auto { display: inline; }"
|
||||||
|
echo ".toggle.light .icon-light { display: inline; }"
|
||||||
|
echo ".toggle.dark .icon-dark { display: inline; }"
|
||||||
echo ".attachments { margin-top: 25px; border: 1px solid var(--border); border-radius: 12px; overflow: hidden; }"
|
echo ".attachments { margin-top: 25px; border: 1px solid var(--border); border-radius: 12px; overflow: hidden; }"
|
||||||
echo ".attachments-header { padding: 12px 20px; background: var(--header-bg); cursor: pointer; font-weight: 500; color: var(--muted); }"
|
echo ".attachments-header { padding: 12px 20px; background: var(--header-bg); cursor: pointer; font-weight: 500; color: var(--muted); }"
|
||||||
echo ".attachments-header:hover { background: var(--border); }"
|
echo ".attachments-header:hover { background: var(--border); }"
|
||||||
|
|
@ -72,9 +76,14 @@ else
|
||||||
echo ".lightbox-close:hover { background: rgba(0,0,0,0.8); }"
|
echo ".lightbox-close:hover { background: rgba(0,0,0,0.8); }"
|
||||||
echo "</style></head><body>"
|
echo "</style></head><body>"
|
||||||
echo '<div class="lightbox" onclick="closeLightbox()"><span class="lightbox-close">×</span><img id="lightbox-img" src="" alt=""></div>'
|
echo '<div class="lightbox" onclick="closeLightbox()"><span class="lightbox-close">×</span><img id="lightbox-img" src="" alt=""></div>'
|
||||||
echo '<button class="toggle" onclick="toggleTheme()" title="Toggle theme"><span class="moon">☾</span><span class="sun">☀</span></button>'
|
echo '<button class="toggle auto" onclick="toggleTheme()" title="Theme: auto"><span class="icon icon-auto">◑</span><span class="icon icon-light">☀</span><span class="icon icon-dark">☾</span></button>'
|
||||||
echo "<script>"
|
echo "<script>"
|
||||||
echo "function toggleTheme() { document.documentElement.classList.toggle('dark'); }"
|
echo "var themes = ['auto', 'light', 'dark'];"
|
||||||
|
echo "function getSystemTheme() { return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; }"
|
||||||
|
echo "function applyTheme(t) { if (t === 'auto') t = getSystemTheme(); document.documentElement.classList.toggle('dark', t === 'dark'); }"
|
||||||
|
echo "function toggleTheme() { var curr = localStorage.getItem('email-theme') || 'auto'; var next = themes[(themes.indexOf(curr) + 1) % 3]; localStorage.setItem('email-theme', next); applyTheme(next); updateToggleBtn(next); }"
|
||||||
|
echo "function updateToggleBtn(t) { var btn = document.querySelector('.toggle'); btn.className = 'toggle ' + t; btn.title = 'Theme: ' + t + ' (click to change)'; }"
|
||||||
|
echo "(function() { var t = localStorage.getItem('email-theme') || 'auto'; applyTheme(t); updateToggleBtn(t); window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', function() { if ((localStorage.getItem('email-theme') || 'auto') === 'auto') applyTheme('auto'); }); })();"
|
||||||
echo "function toggleQuote(el) { var c = el.nextElementSibling; c.classList.toggle('show'); el.textContent = c.classList.contains('show') ? '▼ Hide quoted text' : '▶ Show quoted text'; }"
|
echo "function toggleQuote(el) { var c = el.nextElementSibling; c.classList.toggle('show'); el.textContent = c.classList.contains('show') ? '▼ Hide quoted text' : '▶ Show quoted text'; }"
|
||||||
echo "function toggleAttachments(el) { var c = el.nextElementSibling; c.classList.toggle('show'); var n = el.textContent.match(/\\d+/)[0]; el.textContent = c.classList.contains('show') ? '▼ Attachments (' + n + ')' : '▶ Attachments (' + n + ')'; }"
|
echo "function toggleAttachments(el) { var c = el.nextElementSibling; c.classList.toggle('show'); var n = el.textContent.match(/\\d+/)[0]; el.textContent = c.classList.contains('show') ? '▼ Attachments (' + n + ')' : '▶ Attachments (' + n + ')'; }"
|
||||||
echo "function openLightbox(src) { document.getElementById('lightbox-img').src = src; document.querySelector('.lightbox').classList.add('show'); document.body.style.overflow = 'hidden'; }"
|
echo "function openLightbox(src) { document.getElementById('lightbox-img').src = src; document.querySelector('.lightbox').classList.add('show'); document.body.style.overflow = 'hidden'; }"
|
||||||
|
|
@ -233,7 +242,8 @@ else
|
||||||
result = before "<a href=\"mailto:" email "\">" email "</a>" after
|
result = before "<a href=\"mailto:" email "\">" email "</a>" after
|
||||||
}
|
}
|
||||||
# Third: Handle text<https://url> - text becomes link text
|
# Third: Handle text<https://url> - text becomes link text
|
||||||
while (match(result, /[A-Za-z0-9_-]+<https?:\/\/[^&]+>/)) {
|
# Text can include letters, numbers, and punctuation like /
|
||||||
|
while (match(result, /[A-Za-z0-9_\/.,-]+<https?:\/\/[^&]+>/)) {
|
||||||
before = substr(result, 1, RSTART-1)
|
before = substr(result, 1, RSTART-1)
|
||||||
full = substr(result, RSTART, RLENGTH)
|
full = substr(result, RSTART, RLENGTH)
|
||||||
# Find where < starts
|
# Find where < starts
|
||||||
|
|
@ -251,8 +261,8 @@ else
|
||||||
after = substr(result, RSTART+RLENGTH)
|
after = substr(result, RSTART+RLENGTH)
|
||||||
result = before "<a href=\"" url "\" target=\"_blank\">" url "</a>" after
|
result = before "<a href=\"" url "\" target=\"_blank\">" url "</a>" after
|
||||||
}
|
}
|
||||||
# Fifth: Handle plain URLs (not already in href)
|
# Fifth: Handle plain https:// URLs (not already in href)
|
||||||
while (match(result, /https?:\/\/[^ &<>"\n\t]+/)) {
|
while (match(result, /https?:\/\/[A-Za-z0-9][^ \t\n"<>&]*/)) {
|
||||||
before = substr(result, 1, RSTART-1)
|
before = substr(result, 1, RSTART-1)
|
||||||
if (before ~ /href="$/) break
|
if (before ~ /href="$/) break
|
||||||
url = substr(result, RSTART, RLENGTH)
|
url = substr(result, RSTART, RLENGTH)
|
||||||
|
|
@ -261,7 +271,17 @@ else
|
||||||
sub(/[.,;:!?)]+$/, "", url)
|
sub(/[.,;:!?)]+$/, "", url)
|
||||||
result = before "<a href=\"" url "\" target=\"_blank\">" url "</a>" after
|
result = before "<a href=\"" url "\" target=\"_blank\">" url "</a>" after
|
||||||
}
|
}
|
||||||
# Sixth: Handle [cid:image] inline images
|
# Sixth: Handle www. URLs (without https://)
|
||||||
|
while (match(result, /www\.[A-Za-z0-9][^ \t\n"<>&]*/)) {
|
||||||
|
before = substr(result, 1, RSTART-1)
|
||||||
|
if (before ~ /href="$/ || before ~ /\/\/$/) break
|
||||||
|
url = substr(result, RSTART, RLENGTH)
|
||||||
|
after = substr(result, RSTART+RLENGTH)
|
||||||
|
# Clean trailing punctuation
|
||||||
|
sub(/[.,;:!?)]+$/, "", url)
|
||||||
|
result = before "<a href=\"https://" url "\" target=\"_blank\">" url "</a>" after
|
||||||
|
}
|
||||||
|
# Seventh: Handle [cid:image] inline images
|
||||||
while (match(result, /\[cid:[^\]]+\]/)) {
|
while (match(result, /\[cid:[^\]]+\]/)) {
|
||||||
before = substr(result, 1, RSTART-1)
|
before = substr(result, 1, RSTART-1)
|
||||||
# Extract image name from cid reference
|
# Extract image name from cid reference
|
||||||
|
|
|
||||||
39
config/git/config
Normal file
39
config/git/config
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
[user]
|
||||||
|
name = Ray Andrew
|
||||||
|
email = rs@rs.ht
|
||||||
|
signingkey = ~/.ssh/id_ed25519.pub
|
||||||
|
|
||||||
|
[gpg]
|
||||||
|
format = ssh
|
||||||
|
|
||||||
|
[core]
|
||||||
|
editor = nvim
|
||||||
|
|
||||||
|
[credential]
|
||||||
|
helper = store --file ~/.git-credentials
|
||||||
|
|
||||||
|
[pull]
|
||||||
|
rebase = true
|
||||||
|
|
||||||
|
[github]
|
||||||
|
user = rayandrew
|
||||||
|
|
||||||
|
[commit]
|
||||||
|
gpgsign = true
|
||||||
|
|
||||||
|
[format]
|
||||||
|
signoff = true
|
||||||
|
|
||||||
|
[lfs]
|
||||||
|
repositoryformatversion = 0
|
||||||
|
|
||||||
|
[filter "lfs"]
|
||||||
|
clean = git-lfs clean -- %f
|
||||||
|
smudge = git-lfs smudge -- %f
|
||||||
|
process = git-lfs filter-process
|
||||||
|
required = true
|
||||||
|
|
||||||
|
[alias]
|
||||||
|
c = !~/dotfiles/bin/gc -s
|
||||||
|
cc = !~/dotfiles/bin/gc-check
|
||||||
|
cl = !~/dotfiles/bin/gc-changelog
|
||||||
3
config/git/ignore
Normal file
3
config/git/ignore
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
.DS_Store
|
||||||
|
*~
|
||||||
|
*.swp
|
||||||
|
|
@ -57,6 +57,9 @@ add 'aserowy/tmux.nvim'
|
||||||
add 'NMAC427/guess-indent.nvim'
|
add 'NMAC427/guess-indent.nvim'
|
||||||
add 'wakatime/vim-wakatime'
|
add 'wakatime/vim-wakatime'
|
||||||
add 'OXY2DEV/markview.nvim'
|
add 'OXY2DEV/markview.nvim'
|
||||||
|
add { source = 'R-nvim/R.nvim', depends = { 'nvim-treesitter/nvim-treesitter' } }
|
||||||
|
add 'nvim-treesitter/nvim-treesitter'
|
||||||
|
add 'nvim-treesitter/nvim-treesitter-textobjects'
|
||||||
|
|
||||||
-- color themes
|
-- color themes
|
||||||
add 'EdenEast/nightfox.nvim'
|
add 'EdenEast/nightfox.nvim'
|
||||||
|
|
@ -279,6 +282,112 @@ later(function()
|
||||||
}
|
}
|
||||||
|
|
||||||
require('tmux').setup {}
|
require('tmux').setup {}
|
||||||
|
|
||||||
|
-- Ensure treesitter parsers are installed
|
||||||
|
require('nvim-treesitter.configs').setup {
|
||||||
|
ensure_installed = {
|
||||||
|
'r',
|
||||||
|
'rnoweb',
|
||||||
|
'lua',
|
||||||
|
'python',
|
||||||
|
'rust',
|
||||||
|
'c',
|
||||||
|
'cpp',
|
||||||
|
'javascript',
|
||||||
|
'typescript',
|
||||||
|
'tsx',
|
||||||
|
'json',
|
||||||
|
'yaml',
|
||||||
|
'html',
|
||||||
|
'css',
|
||||||
|
'markdown',
|
||||||
|
'markdown_inline',
|
||||||
|
'nix',
|
||||||
|
'bash',
|
||||||
|
'vim',
|
||||||
|
'vimdoc',
|
||||||
|
'query',
|
||||||
|
'diff',
|
||||||
|
'git_rebase',
|
||||||
|
'gitcommit',
|
||||||
|
},
|
||||||
|
auto_install = true,
|
||||||
|
highlight = { enable = true },
|
||||||
|
incremental_selection = {
|
||||||
|
enable = true,
|
||||||
|
keymaps = {
|
||||||
|
init_selection = 'vv',
|
||||||
|
node_incremental = '<Tab>',
|
||||||
|
scope_incremental = '<C-Tab>',
|
||||||
|
node_decremental = '<S-Tab>',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
textobjects = {
|
||||||
|
select = {
|
||||||
|
enable = true,
|
||||||
|
lookahead = true,
|
||||||
|
keymaps = {
|
||||||
|
['af'] = { query = '@function.outer', desc = 'Select outer function' },
|
||||||
|
['if'] = { query = '@function.inner', desc = 'Select inner function' },
|
||||||
|
['ac'] = { query = '@class.outer', desc = 'Select outer class' },
|
||||||
|
['ic'] = { query = '@class.inner', desc = 'Select inner class' },
|
||||||
|
['aa'] = { query = '@parameter.outer', desc = 'Select outer argument' },
|
||||||
|
['ia'] = { query = '@parameter.inner', desc = 'Select inner argument' },
|
||||||
|
['ai'] = { query = '@conditional.outer', desc = 'Select outer conditional' },
|
||||||
|
['ii'] = { query = '@conditional.inner', desc = 'Select inner conditional' },
|
||||||
|
['al'] = { query = '@loop.outer', desc = 'Select outer loop' },
|
||||||
|
['il'] = { query = '@loop.inner', desc = 'Select inner loop' },
|
||||||
|
['ab'] = { query = '@block.outer', desc = 'Select outer block' },
|
||||||
|
['ib'] = { query = '@block.inner', desc = 'Select inner block' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
move = {
|
||||||
|
enable = true,
|
||||||
|
set_jumps = true,
|
||||||
|
goto_next_start = {
|
||||||
|
[']f'] = { query = '@function.outer', desc = 'Next function start' },
|
||||||
|
[']c'] = { query = '@class.outer', desc = 'Next class start' },
|
||||||
|
[']a'] = { query = '@parameter.inner', desc = 'Next argument' },
|
||||||
|
},
|
||||||
|
goto_next_end = {
|
||||||
|
[']F'] = { query = '@function.outer', desc = 'Next function end' },
|
||||||
|
[']C'] = { query = '@class.outer', desc = 'Next class end' },
|
||||||
|
},
|
||||||
|
goto_previous_start = {
|
||||||
|
['[f'] = { query = '@function.outer', desc = 'Previous function start' },
|
||||||
|
['[c'] = { query = '@class.outer', desc = 'Previous class start' },
|
||||||
|
['[a'] = { query = '@parameter.inner', desc = 'Previous argument' },
|
||||||
|
},
|
||||||
|
goto_previous_end = {
|
||||||
|
['[F'] = { query = '@function.outer', desc = 'Previous function end' },
|
||||||
|
['[C'] = { query = '@class.outer', desc = 'Previous class end' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
swap = {
|
||||||
|
enable = true,
|
||||||
|
swap_next = {
|
||||||
|
['<leader>a'] = { query = '@parameter.inner', desc = 'Swap with next argument' },
|
||||||
|
},
|
||||||
|
swap_previous = {
|
||||||
|
['<leader>A'] = { query = '@parameter.inner', desc = 'Swap with previous argument' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
require('r').setup {
|
||||||
|
hook = {
|
||||||
|
on_filetype = function()
|
||||||
|
vim.keymap.set('n', '<leader>rs', '<Plug>RStart', { buffer = true, desc = 'Start R' })
|
||||||
|
vim.keymap.set('n', '<leader>rq', '<Plug>RClose', { buffer = true, desc = 'Close R' })
|
||||||
|
vim.keymap.set('n', '<leader>rl', '<Plug>RSendLine', { buffer = true, desc = 'Send line to R' })
|
||||||
|
vim.keymap.set('v', '<leader>rs', '<Plug>RSendSelection', { buffer = true, desc = 'Send selection to R' })
|
||||||
|
vim.keymap.set('n', '<leader>rf', '<Plug>RSendFile', { buffer = true, desc = 'Send file to R' })
|
||||||
|
vim.keymap.set('n', '<leader>ro', '<Plug>RShowArgs', { buffer = true, desc = 'Show function args' })
|
||||||
|
vim.keymap.set('n', '<leader>rh', '<Plug>RHelp', { buffer = true, desc = 'R help' })
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
later(function()
|
later(function()
|
||||||
|
|
@ -530,8 +639,15 @@ later(function()
|
||||||
root_markers = { '.marksman.toml', '.git', '.editorconfig' },
|
root_markers = { '.marksman.toml', '.git', '.editorconfig' },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-- R
|
||||||
|
vim.lsp.config.r_language_server = {
|
||||||
|
cmd = { 'R', '--slave', '-e', 'languageserver::run()' },
|
||||||
|
filetypes = { 'r', 'rmd' },
|
||||||
|
root_markers = { '.Rproj', 'DESCRIPTION', '.git' },
|
||||||
|
}
|
||||||
|
|
||||||
-- Enable language servers
|
-- Enable language servers
|
||||||
vim.lsp.enable { 'lua_ls', 'ts_ls', 'pyright', 'rust_analyzer', 'clangd', 'copilot', 'nixd', 'marksman' }
|
vim.lsp.enable { 'lua_ls', 'ts_ls', 'pyright', 'rust_analyzer', 'clangd', 'copilot', 'nixd', 'marksman', 'r_language_server' }
|
||||||
end)
|
end)
|
||||||
|
|
||||||
local map = function(mode, lhs, rhs, opts)
|
local map = function(mode, lhs, rhs, opts)
|
||||||
|
|
@ -650,3 +766,22 @@ vim.api.nvim_create_autocmd({ 'FocusGained', 'BufEnter' }, {
|
||||||
command = "if mode() != 'c' | checktime | endif",
|
command = "if mode() != 'c' | checktime | endif",
|
||||||
pattern = '*',
|
pattern = '*',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
-- Restore gitrebase keybindings (overridden by clipboard=unnamedplus)
|
||||||
|
vim.api.nvim_create_autocmd('FileType', {
|
||||||
|
pattern = 'gitrebase',
|
||||||
|
callback = function()
|
||||||
|
local opts = { buffer = true, silent = true }
|
||||||
|
vim.keymap.set('n', 'p', '<cmd>Pick<cr>', opts)
|
||||||
|
vim.keymap.set('n', 'r', '<cmd>Reword<cr>', opts)
|
||||||
|
vim.keymap.set('n', 'e', '<cmd>Edit<cr>', opts)
|
||||||
|
vim.keymap.set('n', 's', '<cmd>Squash<cr>', opts)
|
||||||
|
vim.keymap.set('n', 'f', '<cmd>Fixup<cr>', opts)
|
||||||
|
vim.keymap.set('n', 'd', '<cmd>Drop<cr>', opts)
|
||||||
|
vim.keymap.set('n', 'x', '<cmd>Exec<cr>', opts)
|
||||||
|
vim.keymap.set('n', '<C-n>', '<cmd>move +1<cr>', opts)
|
||||||
|
vim.keymap.set('n', '<C-p>', '<cmd>move -2<cr>', opts)
|
||||||
|
vim.keymap.set('n', '<C-j>', '<cmd>move +1<cr>', opts)
|
||||||
|
vim.keymap.set('n', '<C-k>', '<cmd>move -2<cr>', opts)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
|
||||||
|
|
@ -439,7 +439,7 @@ local function apply_highlights(c, opts)
|
||||||
hi('MarkviewCode', { bg = c.bg_dark })
|
hi('MarkviewCode', { bg = c.bg_dark })
|
||||||
hi('MarkviewCodeInfo', { fg = c.comment })
|
hi('MarkviewCodeInfo', { fg = c.comment })
|
||||||
hi('MarkviewCodeFg', { fg = c.fg })
|
hi('MarkviewCodeFg', { fg = c.fg })
|
||||||
hi('MarkviewInlineCode', { fg = c.string, bg = c.bg_dark })
|
hi('MarkviewInlineCode', { fg = c.string, bg = c.bg_float })
|
||||||
|
|
||||||
-- Lists
|
-- Lists
|
||||||
hi('MarkviewListItemStar', { fg = c.tag })
|
hi('MarkviewListItemStar', { fg = c.tag })
|
||||||
|
|
|
||||||
244
docs/nvim.md
Normal file
244
docs/nvim.md
Normal file
|
|
@ -0,0 +1,244 @@
|
||||||
|
# Neovim Configuration
|
||||||
|
|
||||||
|
Leader key: `<Space>`
|
||||||
|
|
||||||
|
## Files & Buffers
|
||||||
|
|
||||||
|
|
||||||
|
| Key | Action |
|
||||||
|
|-----|--------|
|
||||||
|
| `<leader>ex` | Toggle file tree |
|
||||||
|
| `<leader>fs` | Save file |
|
||||||
|
| `<leader>ff` | Find file |
|
||||||
|
| `<leader>fr` | Find recent file |
|
||||||
|
| `<leader>sf` | Search git files |
|
||||||
|
| `<leader>bb` | List buffers |
|
||||||
|
| `<leader><leader>` | List buffers |
|
||||||
|
| `<leader>bd` | Delete buffer |
|
||||||
|
| `<leader>.` | Scratch buffer |
|
||||||
|
|
||||||
|
|
||||||
|
## Search
|
||||||
|
|
||||||
|
|
||||||
|
| Key | Action |
|
||||||
|
|-----|--------|
|
||||||
|
| `<leader>sg` | Search grep |
|
||||||
|
| `<leader>sw` | Search grep word |
|
||||||
|
| `<leader>sb` | Buffer lines |
|
||||||
|
| `<leader>sB` | Grep open buffers |
|
||||||
|
| `<leader>:` | Command history |
|
||||||
|
|
||||||
|
|
||||||
|
## LSP
|
||||||
|
|
||||||
|
|
||||||
|
| Key | Action |
|
||||||
|
|-----|--------|
|
||||||
|
| `gd` | Goto definition |
|
||||||
|
| `gD` | Goto declaration |
|
||||||
|
| `gr` | References |
|
||||||
|
| `gI` | Goto implementation |
|
||||||
|
| `gy` | Goto type definition |
|
||||||
|
| `gai` | Incoming calls |
|
||||||
|
| `gao` | Outgoing calls |
|
||||||
|
| `K` | Hover documentation |
|
||||||
|
| `<leader>rn` | Rename symbol |
|
||||||
|
| `<leader>ca` | Code action |
|
||||||
|
| `<leader>d` | Show diagnostic |
|
||||||
|
| `[d` / `]d` | Previous / next diagnostic |
|
||||||
|
| `<leader>ls` | LSP symbols |
|
||||||
|
| `<leader>lS` | LSP workspace symbols |
|
||||||
|
|
||||||
|
|
||||||
|
## Git
|
||||||
|
|
||||||
|
|
||||||
|
| Key | Action |
|
||||||
|
|-----|--------|
|
||||||
|
| `<leader>gg` | Open Neogit |
|
||||||
|
| `<leader>gb` | Git branches |
|
||||||
|
| `<leader>gl` | Git log |
|
||||||
|
| `<leader>gL` | Git log (file) |
|
||||||
|
| `<leader>gs` | Git status |
|
||||||
|
| `<leader>gS` | Git stash |
|
||||||
|
| `<leader>gd` | Git diff (hunks) |
|
||||||
|
| `<leader>gi` | GitHub issues (open) |
|
||||||
|
| `<leader>gI` | GitHub issues (all) |
|
||||||
|
| `<leader>gp` | GitHub PRs (open) |
|
||||||
|
| `<leader>gP` | GitHub PRs (all) |
|
||||||
|
|
||||||
|
|
||||||
|
## Grapple (Harpoon-style marks)
|
||||||
|
|
||||||
|
|
||||||
|
| Key | Action |
|
||||||
|
|-----|--------|
|
||||||
|
| `<leader>ma` | Toggle tag |
|
||||||
|
| `<leader>mm` | Tags menu |
|
||||||
|
| `<leader>m1-4` | Select tag 1-4 |
|
||||||
|
| `<leader>mn` | Next tag |
|
||||||
|
| `<leader>mp` | Previous tag |
|
||||||
|
| `<leader>mga` | Add global tag |
|
||||||
|
| `<leader>mgm` | Global tags menu |
|
||||||
|
|
||||||
|
|
||||||
|
## Claude Code
|
||||||
|
|
||||||
|
|
||||||
|
| Key | Action |
|
||||||
|
|-----|--------|
|
||||||
|
| `<leader>ac` | Toggle Claude |
|
||||||
|
| `<leader>af` | Focus Claude |
|
||||||
|
| `<leader>ar` | Resume Claude |
|
||||||
|
| `<leader>aC` | Continue Claude |
|
||||||
|
| `<leader>am` | Select Claude model |
|
||||||
|
| `<leader>ab` | Add current buffer |
|
||||||
|
| `<leader>as` | Send to Claude (visual) |
|
||||||
|
| `<leader>aa` | Accept diff |
|
||||||
|
| `<leader>ad` | Deny diff |
|
||||||
|
|
||||||
|
|
||||||
|
## Code / Config
|
||||||
|
|
||||||
|
|
||||||
|
| Key | Action |
|
||||||
|
|-----|--------|
|
||||||
|
| `<leader>cc` | Compile |
|
||||||
|
| `<leader>cC` | Recompile |
|
||||||
|
| `<leader>cf` | Format buffer |
|
||||||
|
| `<leader>cr` | Reload config |
|
||||||
|
| `<leader>ce` | Edit config |
|
||||||
|
| `<leader>ch` | Check health |
|
||||||
|
|
||||||
|
|
||||||
|
## Windows
|
||||||
|
|
||||||
|
|
||||||
|
| Key | Action |
|
||||||
|
|-----|--------|
|
||||||
|
| `<leader>wh/j/k/l` | Navigate windows |
|
||||||
|
| `<leader>ws` | Split horizontal |
|
||||||
|
| `<leader>wv` | Split vertical |
|
||||||
|
| `<leader>wq` | Close window |
|
||||||
|
| `<C-h/j/k/l>` | Move to tmux pane |
|
||||||
|
|
||||||
|
|
||||||
|
## Quickfix
|
||||||
|
|
||||||
|
|
||||||
|
| Key | Action |
|
||||||
|
|-----|--------|
|
||||||
|
| `<leader>qq` | Open/focus quickfix |
|
||||||
|
| `<leader>ql` | Toggle loclist |
|
||||||
|
| `>` | Expand quickfix context |
|
||||||
|
| `<` | Collapse quickfix context |
|
||||||
|
|
||||||
|
|
||||||
|
## Utilities
|
||||||
|
|
||||||
|
|
||||||
|
| Key | Action |
|
||||||
|
|-----|--------|
|
||||||
|
| `<leader>ut` | Toggle undotree |
|
||||||
|
| `<leader>zz` | Toggle zen mode |
|
||||||
|
| `<leader>zm` | Toggle zoom |
|
||||||
|
|
||||||
|
|
||||||
|
## R (in R files)
|
||||||
|
|
||||||
|
|
||||||
|
| Key | Action |
|
||||||
|
|-----|--------|
|
||||||
|
| `<leader>rs` | Start R |
|
||||||
|
| `<leader>rq` | Close R |
|
||||||
|
| `<leader>rl` | Send line to R |
|
||||||
|
| `<leader>rs` | Send selection to R (visual) |
|
||||||
|
| `<leader>rf` | Send file to R |
|
||||||
|
| `<leader>ro` | Show function args |
|
||||||
|
| `<leader>rh` | R help |
|
||||||
|
|
||||||
|
|
||||||
|
## Treesitter
|
||||||
|
|
||||||
|
### Incremental Selection
|
||||||
|
|
||||||
|
Select code based on AST nodes - expand or shrink selection intelligently.
|
||||||
|
|
||||||
|
|
||||||
|
| Key | Action |
|
||||||
|
|-----|--------|
|
||||||
|
| `vv` | Start treesitter selection |
|
||||||
|
| `<Tab>` | Expand to larger node |
|
||||||
|
| `<S-Tab>` | Shrink to smaller node |
|
||||||
|
| `<C-Tab>` | Expand to full scope |
|
||||||
|
|
||||||
|
|
||||||
|
### Text Objects
|
||||||
|
|
||||||
|
Use with operators like `v` (visual), `d` (delete), `c` (change), `y` (yank).
|
||||||
|
|
||||||
|
|
||||||
|
| Key | Description |
|
||||||
|
|-----|-------------|
|
||||||
|
| `af` / `if` | Outer / inner function |
|
||||||
|
| `ac` / `ic` | Outer / inner class |
|
||||||
|
| `aa` / `ia` | Outer / inner argument |
|
||||||
|
| `ai` / `ii` | Outer / inner conditional |
|
||||||
|
| `al` / `il` | Outer / inner loop |
|
||||||
|
| `ab` / `ib` | Outer / inner block |
|
||||||
|
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
- `vaf` - select entire function
|
||||||
|
- `dif` - delete function body
|
||||||
|
- `caa` - change an argument
|
||||||
|
- `yic` - yank inner class
|
||||||
|
|
||||||
|
### Movement
|
||||||
|
|
||||||
|
Jump between functions, classes, and arguments.
|
||||||
|
|
||||||
|
|
||||||
|
| Key | Action |
|
||||||
|
|-----|--------|
|
||||||
|
| `]f` / `[f` | Next / previous function start |
|
||||||
|
| `]F` / `[F` | Next / previous function end |
|
||||||
|
| `]c` / `[c` | Next / previous class start |
|
||||||
|
| `]C` / `[C` | Next / previous class end |
|
||||||
|
| `]a` / `[a` | Next / previous argument |
|
||||||
|
|
||||||
|
|
||||||
|
### Swap
|
||||||
|
|
||||||
|
|
||||||
|
| Key | Action |
|
||||||
|
|-----|--------|
|
||||||
|
| `<leader>a` | Swap argument with next |
|
||||||
|
| `<leader>A` | Swap argument with previous |
|
||||||
|
|
||||||
|
|
||||||
|
## Git Rebase (in gitrebase files)
|
||||||
|
|
||||||
|
|
||||||
|
| Key | Action |
|
||||||
|
|-----|--------|
|
||||||
|
| `p` | Pick |
|
||||||
|
| `r` | Reword |
|
||||||
|
| `e` | Edit |
|
||||||
|
| `s` | Squash |
|
||||||
|
| `f` | Fixup |
|
||||||
|
| `d` | Drop |
|
||||||
|
| `x` | Exec |
|
||||||
|
| `<C-n>` / `<C-j>` | Move line down |
|
||||||
|
| `<C-p>` / `<C-k>` | Move line up |
|
||||||
|
|
||||||
|
|
||||||
|
## Terminal
|
||||||
|
|
||||||
|
|
||||||
|
| Key | Action |
|
||||||
|
|-----|--------|
|
||||||
|
| `<C-q>` | Exit terminal mode |
|
||||||
|
|
||||||
|
|
@ -148,6 +148,7 @@
|
||||||
treefmtEval.${system}.config.build.wrapper
|
treefmtEval.${system}.config.build.wrapper
|
||||||
];
|
];
|
||||||
packages = with pkgs; [
|
packages = with pkgs; [
|
||||||
|
git
|
||||||
sops
|
sops
|
||||||
age
|
age
|
||||||
ssh-to-age
|
ssh-to-age
|
||||||
|
|
|
||||||
44
home/git.nix
44
home/git.nix
|
|
@ -1,52 +1,16 @@
|
||||||
{
|
{
|
||||||
pkgs,
|
pkgs,
|
||||||
config,
|
config,
|
||||||
user,
|
dots,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
let
|
|
||||||
home = config.home.homeDirectory;
|
|
||||||
in
|
|
||||||
{
|
{
|
||||||
home.packages = with pkgs; [
|
home.packages = with pkgs; [
|
||||||
gh
|
gh
|
||||||
|
git-lfs
|
||||||
];
|
];
|
||||||
|
|
||||||
programs.git = {
|
xdg.configFile = {
|
||||||
enable = true;
|
"git".source = config.lib.file.mkOutOfStoreSymlink "${dots}/config/git";
|
||||||
lfs = {
|
|
||||||
enable = true;
|
|
||||||
};
|
};
|
||||||
settings = {
|
|
||||||
user = {
|
|
||||||
name = "Ray Andrew";
|
|
||||||
email = "rs@rs.ht";
|
|
||||||
};
|
|
||||||
gpg.format = "ssh";
|
|
||||||
core.editor = "emacs";
|
|
||||||
credential.helper = "store --file ${home}/.git-credentials";
|
|
||||||
pull.rebase = true;
|
|
||||||
github.user = user;
|
|
||||||
};
|
|
||||||
signing = {
|
|
||||||
key = "${home}/.ssh/id_ed25519.pub";
|
|
||||||
signByDefault = true;
|
|
||||||
signer = "";
|
|
||||||
};
|
|
||||||
ignores = [
|
|
||||||
".DS_Store"
|
|
||||||
"*~"
|
|
||||||
"*.swp"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
# programs.gh = {
|
|
||||||
# enable = true;
|
|
||||||
# settings = {
|
|
||||||
# git_protocol = "ssh";
|
|
||||||
# };
|
|
||||||
# extensions = with pkgs; [
|
|
||||||
# gh-copilot
|
|
||||||
# ];
|
|
||||||
# };
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue