Skip to content

Instantly share code, notes, and snippets.

@brand-it
Created August 26, 2021 20:09
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save brand-it/82f1864e4cf3ade5228307c6dc7131e8 to your computer and use it in GitHub Desktop.
Save brand-it/82f1864e4cf3ade5228307c6dc7131e8 to your computer and use it in GitHub Desktop.
#!/bin/bash
set -e
# Instructions:
#
# This script is a Git pre-commit hook that spell checks any content you are about to commit.
#
# Place this script into the ".git/hooks/" directory in your repository. It must be called "pre-commit" and be
# executable. A Git hook only works in a single repository. You need to copy this hook into every repository you wish to
# use it in manually. Optionally, you can set up a symlink in the ".git/hooks/" directory pointing to the script.
#
# Each time you try to commit something, this script is run and spell checks the content you are committing.
#
# Should you want to bypass the pre-commit hook (though not recommended), you can commit with "git commit --no-verify".
# The following is a text file that represents your custom dictionary; edit as necessary. Add words to it that you wish
# to ignore for the spell check.
dict=~/Qsync/.git-spell-check
if [ ! -f $dict ]; then
touch ~/Qsync/.git-spell-check
dict=~/Qsync/.git-spell-check
printf "%s\n" "Custom dictionary not found. Created ~/Qsync/.git-spell-checkk..."
fi
# The following is a temporary dictionary (a binary file) created from the dict text file. It is deleted after the
# script finishes.
temp_dict=$(mktemp docs-dictionary-XXXXXX)
# Language of your doc. When using a non-English language, make sure you have the appropriate aspell libraries
# installed: "yum search aspell". For example, to spell check in Slovak, you must have the aspell-sk package installed.
lang=en
# Define an extension for any additional dictionaries (containing words that are ignored during the spell check) that
# are kept locally in your repository. These dictionaries will be loaded on top of the existing global dictionary (by
# default ~/.git-spell-check).
extension=pws
# Clean up if script is interrupted or terminated.
trap "cleanup" SIGINT SIGTERM
# Prepares the dictionary from scratch in case new words were added since last time.
function prepare_dictionary() {
local_dict=$(find . -name *.$extension -exec ls {} \;)
if [ -z "$local_dict" ]; then
sort -u $temp_dict -o $temp_dict
aspell --lang="$lang" create master "$temp_dict" < "$dict"
else
temp_file=$(mktemp temp_file-XXXXXX)
for file in $local_dict; do
cat $file >> $temp_file
done
cat $dict >> $temp_file
sort -u $temp_file -o $temp_file
aspell --lang="$lang" create master "$temp_dict" < "$temp_file"
/bin/rm -f "$temp_file"
fi
}
# Removes the temporary dictionary.
function cleanup() {
/bin/rm -f "$temp_dict"
}
# Spell checks content you're about to commit. Writes out words that are misspelled or exits with 0 (i.e. continues with
# commit).
function spell_check() {
words=$(git diff --cached | grep -e "^+[^+]" | aspell --mode=sgml list --add-sgml-skip={ulink,code,literal,firstname,parameter,option,package,replaceable,programlisting,userinput,screen,filename,command,computeroutput,abbrev,accel,orgname,surname,foreignphrase,acronym,hardware,keycap,systemitem,application} --lang="$lang" --extra-dicts="$temp_dict" | sort -u)
if [ ! "$words" ]; then
printf "%s\n" "No typos found. Proceeding with commit..."
cleanup; exit 0
fi
printf "%s\n" "Spell check failed on the following words:
-------------------------------------------------"
echo $words
for word in $words; do
grep --color=always --exclude-dir={.git,tmp} -HIrone "\<$word\>" $(git diff --cached --name-only --diff-filter=ACMRTUXB) | awk -F ":" '{print "File: " $1 "\ton line: " $2 "\tTypo: " $3}'
printf "%s\n" "-------------------"
done
}
# Adds all, some, or none of the misspelled words to the custom dictionary.
function add_words_to_dict() {
printf "%s\n" "
Add any of the misspelled words into your custom dictionary?
* a[ll] (add all words into dict, continue with commit)
* s[ome] (add some words into dict, fix others, no commit)
* i[gnore] (add some words into dict, ignore rest, continue with commit)
* n[one] (no commit)
"
while true; do
exec < /dev/tty # Simply reading user input does not work because Git hooks have stdin detached.
read answer
shopt -s nocasematch
case "$answer" in
a|all)
add_all
cleanup; exit 0
;;
s|some)
add_some
printf "%s\n" "Please fix remaining typos, use \"git add\" to add fixed files, and commit."
cleanup; exit 1
;;
i|ignore)
add_some
cleanup; exit 0
;;
n|none)
add_none
cleanup; exit 1
;;
*)
printf "%s\n" "Incorrect answer. Try again."
continue
esac
shopt -u nocasematch
done
}
# Adds all words to the custom dictionary and continues with the commit.
function add_all() {
for word in $words; do
echo $word >> "$dict"
done
}
# Adds some (selected by user) of the words to the dictinary and exits with 1.
function add_some() {
for word in $words; do
printf "%s\n" "Do you want to add the following word to your custom dictionary: $word (y[es] or n[o])"
while true; do
exec < /dev/tty
read answer
shopt -s nocasematch
case "$answer" in
y|yes)
echo $word >> "$dict"
printf "%s\n" "\"$word\" added to your custom dictionary."
break ;;
n|no)
break ;;
*)
printf "%s\n" "Incorrect answer. Try again."
continue
esac
shopt -u nocasematch
done
done
}
# Adds none of the words and exits with 1.
function add_none() {
printf "%s\n" "No words were added to your custom dictionary."
printf "%s\n" "Please fix remaining typos, use \"git add\" to add fixed files, and commit."
}
prepare_dictionary
spell_check
add_words_to_dict
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment