Separate completion metadata from literal choices

This commit is contained in:
2026-11-03 13:12:05 +00:00
parent f39f27b5cd
commit b83c0a0dfc
3 changed files with 53 additions and 35 deletions

View File

@@ -28,12 +28,15 @@ func CompletionReply(result commandmeta.CompletionResult) (string, error) {
default:
return "", fmt.Errorf("unsupported completion suggestion strategy")
}
var output strings.Builder
if result.Kind != commandmeta.CompletionNone && result.Prefix != "" {
output.WriteString(completionPathPrefixMarker)
output.WriteString(result.Prefix)
output.WriteByte('\n')
// The first line is always metadata, even for a whole-word replacement.
// Remaining lines before the directive are exclusively literal candidates.
if strings.ContainsAny(result.Prefix, "\x00\r\n\t") {
return "", fmt.Errorf("unrepresentable completion prefix")
}
var output strings.Builder
output.WriteString(completionPathPrefixMarker)
output.WriteString(result.Prefix)
output.WriteByte('\n')
if result.Kind == commandmeta.CompletionChoices {
for _, choice := range result.Choices {
if choice.Value == "" || strings.ContainsAny(choice.Value, "\x00\r\n\t") {
@@ -145,20 +148,29 @@ func adjustBashCompletion(script, name string) (string, error) {
if err != nil {
return "", err
}
parsePrefix := `if [[ ${out} == ` + completionPathPrefixMarker + `-* ]]; then
completionPrefix=${out%%$'\n'*}
completionPrefix=${completionPrefix#` + completionPathPrefixMarker + `}
completionOriginalCur=$cur
if [[ $completionOriginalCur == "$completionPrefix"* ]]; then
if [[ $out == *$'\n'* ]]; then out=${out#*$'\n'}; else out=; fi
cur=${completionOriginalCur#"$completionPrefix"}
else
completionPrefix=""
fi
parsePrefix := `if [[ ${out} != ` + completionPathPrefixMarker + `* ]]; then
compopt +o default
return
fi
completionPrefix=${out%%$'\n'*}
completionPrefix=${completionPrefix#` + completionPathPrefixMarker + `}
completionOriginalCur=$cur
if [[ $completionOriginalCur != "$completionPrefix"* ]]; then
compopt +o default
return
fi
if [[ $out == *$'\n'* ]]; then out=${out#*$'\n'}; else out=; fi
cur=${completionOriginalCur#"$completionPrefix"}
# Separate activeHelp from normal completions`
script, err = replaceCompletionText(script, `# Separate activeHelp from normal completions`, parsePrefix)
if err != nil {
return "", err
}
script, err = replaceCompletionSection(script, " local activeHelpMarker=", "\n}\n", ` while IFS='' read -r comp; do
[[ -z $comp ]] || completions+=("$comp")
done <<<"${out}"
`)
if err != nil {
return "", err
}
@@ -196,19 +208,23 @@ func adjustZshCompletion(script string) (string, error) {
if err != nil {
return "", err
}
script, err = replaceCompletionText(script, `local activeHelpMarker="_activeHelp_ "`, `if [[ "$out" == `+completionPathPrefixMarker+`-* ]]; then
local pathPrefix="${out%%$'\n'*}"
pathPrefix="${pathPrefix#`+completionPathPrefixMarker+`}"
if [[ "$lastParam" == "$pathPrefix"* ]]; then
if [[ $out == *$'\n'* ]]; then out="${out#*$'\n'}"; else out=; fi
flagPrefix="$pathPrefix"
fi
fi
script, err = replaceCompletionSection(script, ` local activeHelpMarker=`, " if [ $((directive & shellCompDirectiveNoSpace))", ` [[ "$out" == `+completionPathPrefixMarker+`* ]] || return 1
flagPrefix="${out%%$'\n'*}"
flagPrefix="${flagPrefix#`+completionPathPrefixMarker+`}"
[[ "$lastParam" == "$flagPrefix"* ]] || return 1
if [[ $out == *$'\n'* ]]; then out="${out#*$'\n'}"; else out=; fi
if [[ -n "$flagPrefix" ]]; then
prefixArgs=(-P "$flagPrefix")
fi
local tab="$(printf '\t')"
while IFS= read -r comp; do
if [[ -n "$comp" ]]; then
comp=${comp//:/\\:}
completions+=("${comp//$tab/:}")
fi
done <<<"$out"
local activeHelpMarker="_activeHelp_ "`)
`)
if err != nil {
return "", err
}
@@ -272,15 +288,14 @@ func adjustFishCompletion(script, name string) (string, error) {
if err != nil {
return "", err
}
script, err = replaceCompletionSection(script, " # For Fish, when completing a flag", ` printf "%s\n" "$directiveLine"`, ` set -l flagPrefix ""
if set -q comps[1]; and string match -q -- '`+completionPathPrefixMarker+`-*' "$comps[1]"
set -l candidatePrefix (string replace -- '`+completionPathPrefixMarker+`' '' "$comps[1]")
if test (string sub -l (string length -- "$candidatePrefix") -- "$lastArg") = "$candidatePrefix"
set flagPrefix "$candidatePrefix"
set -e comps[1]
end
script, err = replaceCompletionSection(script, " # For Fish, when completing a flag", ` printf "%s\n" "$directiveLine"`, ` if not set -q comps[1]; or not string match -q -- '`+completionPathPrefixMarker+`*' "$comps[1]"
printf ":%s\n" `+fmt.Sprint(spfcobra.ShellCompDirectiveError)+`
return
end
printf "%s%s\n" '`+completionPathPrefixMarker+`' "$flagPrefix"
set -l flagPrefix (string replace -- '`+completionPathPrefixMarker+`' '' "$comps[1]")
set -q flagPrefix[1]; or set flagPrefix ""
printf "%s\n" "$comps[1]"
set -e comps[1]
for comp in $comps
printf "%s%s\n" "$flagPrefix" "$comp"
end