Skip to content

Commit 6162e72

Browse files
refactored to remove any external tools, supports multiline and quote mixtures.
1 parent fb545c0 commit 6162e72

File tree

1 file changed

+125
-52
lines changed

1 file changed

+125
-52
lines changed

gitprofiles.plugin.zsh

Lines changed: 125 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -21,79 +21,153 @@ function __gitprofiles_hook() {
2121
return 1
2222
fi
2323

24+
# Ensure glob patterns that don't match don't cause errors
25+
setopt LOCAL_OPTIONS NO_NOMATCH
26+
2427
## Load all stored profiles
25-
local profiles=($(grep -o '\[profile [^]]*\]' ${profile_filepath} | tr -d '[]" ' | sed 's/profile//g' | tr '\n' ' '))
28+
local profiles=()
29+
local current_section=""
30+
31+
while IFS= read -r line; do
32+
# Skip empty lines and comments
33+
[[ -z "$line" || "$line" == \#* ]] && continue
34+
35+
# Check for profile section
36+
if [[ "$line" =~ '^\[profile[[:space:]]+"([^"]+)"\]' ]]; then
37+
current_section="${match[1]}"
38+
profiles+=("$current_section")
39+
[[ -n "${GP_DEBUG}" ]] && print -u2 "Found profile: ${current_section}"
40+
fi
41+
done < "${profile_filepath}"
2642

2743
## Check if default profile exists
28-
if [[ ! "${profiles}" =~ "default" ]]; then
29-
echo "gitprofiles: 'default' profile not found in '${profile_filepath}'"
44+
if [[ ! "${profiles[(r)default]}" ]]; then
45+
print -u2 "gitprofiles: 'default' profile not found in '${profile_filepath}'"
3046
return 1
3147
fi
3248

33-
# Ensure glob patterns that don't match don't cause errors
34-
setopt LOCAL_OPTIONS NO_NOMATCH
35-
36-
# Function to parse paths into an array and support tidle expansion
49+
# Function to parse paths into an array and support tilde expansion
3750
function __parse_paths() {
38-
local raw_paths="${(j:\n:)@}" # join args with newlines
39-
local temp=(${(s:,:)raw_paths}) # split on commas first
40-
# Now split each part by newlines
41-
local paths=()
42-
for part in $temp; do
43-
paths+=(${(f)part}) # split on newlines
51+
local raw_paths="$1"
52+
# [[ -n "${GP_DEBUG}" ]] && print -u2 "Raw paths input: ${(q)raw_paths}"
53+
54+
local -a path_lines
55+
# split on commas or newlines
56+
path_lines=(${(s:,:)${(f)raw_paths}})
57+
58+
# Process each line
59+
local -a paths
60+
local line
61+
for line in $path_lines; do
62+
# remove newlines
63+
line="${line//'\n'/}"
64+
65+
# remove quotes
66+
line="${line//[\"\']}"
67+
68+
# Remove trailing commas
69+
line="${line%%,}"
70+
71+
# Trim whitespace
72+
line="${line## }"
73+
line="${line%% }"
74+
75+
# Expand tildes
76+
if [[ $line = "~"* ]]; then
77+
line=${HOME}${line#"~"}
78+
fi
79+
80+
# Skip empty lines
81+
[[ -n "$line" ]] && paths+=("$line")
4482
done
4583

46-
# Process each path
47-
paths=(${paths##[[:space:]]}) # Trim leading spaces
48-
paths=(${paths%%[[:space:]]}) # Trim trailing spaces
49-
paths=(${~paths}) # Expand tilde
50-
paths=(${paths:#}) # Remove empty elements
84+
# Expand tildes
85+
# paths=(${~paths}) # this doesnt work as it expands the glob
5186

52-
echo ${paths}
53-
}
87+
# [[ -n "${GP_DEBUG}" ]] && print -u2 "Final paths: ${paths}"
88+
print -l -- ${paths}
89+
}
5490

55-
## Iterate over all profiles to get the name, email, signingkey and path
91+
## Parse configuration for each profile
5692
for profile in ${profiles}; do
5793
typeset -A profile_value_map
94+
local in_current_profile=0
95+
local in_paths=0
96+
local paths_tmp=()
97+
98+
while IFS= read -r line; do
99+
# Skip empty lines and comments
100+
[[ -z "$line" || "$line" == \#* ]] && continue
101+
102+
# Check for profile section
103+
if [[ "$line" =~ '^\[profile[[:space:]]+"([^"]+)"\]' ]]; then
104+
if [[ "${match[1]}" == "$profile" ]]; then
105+
in_current_profile=1
106+
else
107+
in_current_profile=0
108+
in_paths=0
109+
fi
110+
continue
111+
fi
112+
113+
# Only process lines for current profile
114+
(( in_current_profile )) || continue
115+
116+
# Parse key-value pairs
117+
if [[ "$line" =~ '^[[:space:]]*([^=]+)[[:space:]]*=[[:space:]]*(.*)' ]]; then
118+
local key="${match[1]## }" # Trim leading spaces
119+
key="${key%% }" # Trim trailing spaces
120+
local value="${match[2]}" # Keep spaces in value for now
121+
122+
case "$key" in
123+
name|email|signingkey)
124+
# Remove quotes and trim for non-path values
125+
value="${value## }" # Trim leading spaces
126+
value="${value%% }" # Trim trailing spaces
127+
value="${value#[\"\']}" # Remove leading quote
128+
value="${value%[\"\']}" # Remove trailing quote
129+
profile_value_map[$key]="$value"
130+
;;
131+
path|paths)
132+
in_paths=1
133+
paths_tmp=("$value")
134+
;;
135+
esac
136+
elif (( in_paths )) && [[ "$line" =~ '^[[:space:]]+(.*)' ]]; then
137+
# Handle indented continuation lines for paths
138+
local value="${match[1]}"
139+
paths_tmp+=("$value")
140+
fi
141+
done < "${profile_filepath}"
58142

59-
while read -r key value; do
60-
case "${key}" in
61-
name|email|signingkey)
62-
profile_value_map[${key}]="${value}"
63-
;;
64-
path|paths)
65-
profile_value_map[paths]="${value}"
66-
;;
67-
esac
68-
done < <(awk -F ' = ' '/^\[profile/{p=0} /^\[profile "[^"]*'"${profile}"'"/{p=1} p {gsub(/"/, "", $2); print $1,$2}' ${profile_filepath})
69-
70-
# Parse paths
71-
if [[ -n "${profile_value_map[paths]}" ]]; then
72-
profile_paths_map[${profile}]="$(__parse_paths "${profile_value_map[paths]}")"
143+
# Join and parse paths
144+
if (( ${#paths_tmp} > 0 )); then
145+
local joined_paths="${(j:\n:)paths_tmp}"
146+
profile_paths_map[$profile]="${(@f)$(__parse_paths "$joined_paths")}"
73147
fi
74148

75-
profile_cfg_map[${profile}.name]="${profile_value_map[name]}"
76-
profile_cfg_map[${profile}.email]="${profile_value_map[email]}"
149+
# Store other configurations
150+
profile_cfg_map[$profile.name]="${profile_value_map[name]}"
151+
profile_cfg_map[$profile.email]="${profile_value_map[email]}"
77152

78-
if [[ -n "${profile[signingkey]}" ]]; then
79-
profile_cfg_map[${profile}.signingkey]="${profile_value_map[signingkey]}"
153+
if [[ -n "${profile_value_map[signingkey]}" ]]; then
154+
profile_cfg_map[$profile.signingkey]="${profile_value_map[signingkey]}"
80155
fi
81156
done
82157

83158
# Get current directory
84159
local current_dir=$(pwd)
85160
local matched_profile="default"
86161

87-
[[ -n "${DEBUG}" ]] && echo "Current directory: ${current_dir}"
162+
[[ -n "${GP_DEBUG}" ]] && print -u2 "Current directory: ${current_dir}"
88163

89164
# Check if current directory matches any profile paths
90165
for profile in ${(k)profile_paths_map}; do
91-
[[ -n "${DEBUG}" ]] && echo "Testing Profile: ${profile}"
92-
93-
local paths=(${=profile_paths_map[${profile}]}) # Convert to array
166+
[[ -n "${GP_DEBUG}" ]] && print -u2 "Testing Profile: ${profile}"
94167

168+
local paths=(${=profile_paths_map[$profile]}) # Convert to array
95169
for path_pattern in $paths; do
96-
[[ -n "${DEBUG}" ]] && echo "Checking path pattern: ${path_pattern}"
170+
[[ -n "${GP_DEBUG}" ]] && print -u2 "Testing path pattern: ${path_pattern}"
97171

98172
if [[ "${current_dir}" =~ "${path_pattern}" ]]; then
99173
matched_profile="${profile}"
@@ -111,17 +185,16 @@ function __gitprofiles_hook() {
111185
git config --global user.signingkey "${profile_cfg_map[${matched_profile}.signingkey]}"
112186
fi
113187

114-
# Print debug information if DEBUG is set
115-
if [[ -n "${DEBUG}" ]]; then
116-
echo "Matched profile: ${matched_profile}"
117-
echo "Using configuration:"
118-
echo " name: ${profile_cfg_map[${matched_profile}.name]}"
119-
echo " email: ${profile_cfg_map[${matched_profile}.email]}"
188+
# Print debug information if GP_DEBUG is set
189+
if [[ -n "${GP_DEBUG}" ]] && [[ -n "${matched_profile}" ]]; then
190+
print -u2 "Matched profile: ${matched_profile}"
191+
print -u2 "Using configuration:"
192+
print -u2 " name: ${profile_cfg_map[${matched_profile}.name]}"
193+
print -u2 " email: ${profile_cfg_map[${matched_profile}.email]}"
120194
if [[ -n "${profile_cfg_map[${matched_profile}.signingkey]}" ]]; then
121-
echo " signingkey: ${profile_cfg_map[${matched_profile}.signingkey]}"
195+
print -u2 " signingkey: ${profile_cfg_map[${matched_profile}.signingkey]}"
122196
fi
123197
fi
124198
}
125199

126-
127200
add-zsh-hook chpwd __gitprofiles_hook

0 commit comments

Comments
 (0)