Skip to content

Instantly share code, notes, and snippets.

@imaami
Last active April 26, 2025 17:28
Show Gist options
  • Save imaami/5acdbca38b1f49468f680a1aa04493fe to your computer and use it in GitHub Desktop.
Save imaami/5acdbca38b1f49468f680a1aa04493fe to your computer and use it in GitHub Desktop.
Let GPT describe how your code sucks.
#!/usr/bin/env bash
#
# IMPORTANT: The HTTP authorization header must be found
# in ~/.openai in full, not just the API key.
#
# Usage: ./documentarist.sh [options] /path/to/file.c
#
# Requirements: dos2unix, cpp, clang-format, jq, curl
#
unset a all arg blind files model name no_opt src sym tmp
declare -i all=0 blind=0 no_opt=0
declare -A arg
declare -a files
unknown() {
printf 'error: unknown option: %s\n' "$1" >&2
exit 1
}
set_once() {
local key="$1" val="${2// /}" opt="$3"
if [[ "${arg[$key]}" ]]; then
printf 'error: duplicate option: %s\n' "$opt" >&2
exit 1
fi
if [[ -z "$val" ]]; then
printf 'error: missing argument: %s\n' "$opt" >&2
exit 1
fi
arg["$key"]="$val"
}
while (( $# )); do
if (( no_opt )); then
files+=("$1")
else
a="$1"
while ((1)); do
case "$a" in
--)
no_opt=1 ;;
--all|--blind)
arg["${a:2:1}"]=1 ;;
--language|-x)
set_once x "$2" "$a"; shift ;;
--language=*)
set_once x "${a#*=}" "${a%%=*}" ;;
--model|-m)
set_once m "$2" "$a"; shift ;;
--model=*)
set_once m "${a#*=}" "${a%%=*}" ;;
--symbol|-s)
set_once s "$2" "$a"; shift ;;
--symbol=*)
set_once s "${a#*=}" "${a%%=*}" ;;
--*)
unknown "${a%%=*}" ;;
-[ab]*)
arg["${a:1:1}"]=1
[[ -z "${a:2}" ]] || {
a="-${a:2}"
continue
} ;;
-[msx]*)
set_once "${a:1:1}" "${a:2}" "${a::2}" ;;
-*)
unknown "${a::2}" ;;
*)
files+=("$1") ;;
esac
break
done
fi
shift
done
all="${arg['a']}"
blind="${arg['b']}"
lang="${arg['x']}"
model="${arg['m']:-gpt-4.1}"
sym="${arg['s']}"
if [[ -z "$sym" ]]; then
all=1
elif (( all )); then
echo 'error: options --all and --symbol conflict' >&2
exit 1
fi
[[ -f "${files[0]}" ]] &&
name="${files[0]##*/}" &&
{
[[ "$lang" ]] || {
case "${name##*\.}" in
[ch]pp) lang=c++ ;;
*) lang=c ;;
esac
}
} &&
tmp=$(mktemp -d) && [[ -d "$tmp" ]] || exit 1
src="$tmp/$name"
dos2unix < "${files[0]}" > "$src"
if (( blind )); then
cpp -fpreprocessed -dD -P -o "$src.i" "$src" 2>/dev/null
if [[ -f "$src.i" ]]; then
mv "$src.i" "$src"
fi
fi
[[ -f "$src" ]] || { rm -fr "$tmp"; exit 1; }
clang-format --style='{BasedOnStyle: webkit, IndentWidth: 8,
TabWidth: 8, UseTab: ForIndentation}' -i "$src"
if (( all )); then
sym=",{\"role\":\"user\",\"content\":\"Document all undocumented symbols.\"}"
elif [[ "$sym" ]]; then
sym="$(sed -E 's/([[:blank:]]*\.)?[[:blank:]]*$//' <<< "$sym")"
if [[ "$sym" ]]; then
sym=",{\"role\":\"user\",\"content\":\"Document \`${sym//\"/\\\"}\`.\"}"
fi
fi
jq --rawfile source <(
printf '```\n/** @file %s\n */\n' "$name"
cat "$src"
printf '\n```'
) -Mnc --rawfile system <(cat << EOF
You document C/C++ code using Doxygen syntax. You are given the code and told which function, type, macro, etc. to document. Write the requested documentation and print the documented declarations.
To be clear: you read code that contains full definitions, but you print only declarations with documentation added on top. For example
\`\`\`$lang
int foo (int *bar) {
*bar *= 9;
return *bar ^ ~0;
}
\`\`\`
might become
\`\`\`$lang
/**
* @brief Toggle bar.
* @param bar The bar to modify.
* @return Result status.
* @retval 0 no error.
* @retval EFAULT @p bar is an invalid address.
*/
int foo (int *bar);
\`\`\`
Important:
- Always read and reason about the code itself, describe what it actually does.
- Symbol names aren't guaranteed to be descriptive, don't assume they reflect code behavior.
- Existing comments may be substandard, always verify by reading the code before trusting them.
- Read and understand the program flow, describe all possible results given all possible inputs.
About formatting:
- Not all things that you are asked to document will be functions. Pay attention to what the symbol actually is.
- Your output will be placed in header files. Do not waste space by repeating lines from the input other than the relevant declaration.
- Do not print a function body when documenting a function. Output stops after the argument list's closing parenthesis and a semicolon.
- Macro documentation can include a definition if it is short. Otherwise the documentation, macro name, and args list (if any) suffice.
- Each "@retval" describes one possible return value, no more. Add multiple "@retval" lines if necessary.
- "@retval" does not exclude "@return".
EOF
) '{
"model": "'"$model"'",
"stream": false,
"messages": [
{"role":"system","content":$system},
{"role":"user","content":$source}'"$sym"'
]
}' \
| curl https://api.openai.com/v1/chat/completions -s -S -d @- \
-H @"$HOME/.openai" -H Content-Type:\ application/json \
| jq -r '.choices[0].message.content' \
| tee "documented_$(date -u -Isec).h"
rm -fr "$tmp"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment