
# colourshell.sh

# In an interactive Bash shell, cycles through a sequence of colours,
# one per command, so that you can more easily see where one finishes
# and the next starts.

# (C) 2006 Philip Endecott
# See http://chezphil.org/colourshell/ for more information.

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.


# As far as I'm aware this won't work in any shells other than bash
# because it uses bash-specific features.

# To try it out, source this file into an interactive shell:
# source /path/to/colourshell.sh

# For permanent use this file should be sourced from your bash startup
# script(s).
#
# Bash may execute various different startup scripts; see the
# INVOCATION section of the bash man page for details.  To have this
# script called for both login shells (e.g. virtual consoles) and
# non-login shells (e.g. xterms) you need to call it from both
# .bash_profile and .bashrc.  .bashrc may also be called for
# non-interactive shells; to avoid running this in that case you can
# enclose the source command in an if block that tests for
# interactivity by checking for PS1:
#
# if [ "$PS1" ]
# then
#   source /path/to/colourshell.sh
# fi

# Source this from somewhere near the end of those files.  In
# particular, source it from after any code that sets the prompt
# variables (PS1 etc.) since this code adds its stuff to the existing
# PS1 setting.

# If your startup scripts don't set PS1 to PS4, things could get
# interesting if you start one shell inside another: this code will be
# called twice!  It should still work, but ideally this script would
# scrub any existing colour-setting codes from the prompts before
# adding its own.

# This code generates colour-changing escape sequences using the tput
# program.  This is a simple utility that uses the termcap database to
# look up escape sequences for the current terminal.  It seems to be
# included in the major Linux distributions.  For details, see "man
# tput" and "man 5 terminfo".  In particular see the "Color Handling"
# section of "man 5 terminfo".

# Most terminals with colour support have 8 colours.  An exception is
# rxvt-unicode (and other rxvts?) which claims to have 88.  This script
# doesn't know what to do with any other cases, so gives up immediately
# without error.

ncolours=`tput colors`
case $ncolours in
8) ;;
88) ;;
*) return 0 ;;
esac

# The following are the standard colour codes; see "man 5 terminfo".

setaf_black=`tput setaf 0`
setaf_blue=`tput setaf 1`
setaf_green=`tput setaf 2`
setaf_cyan=`tput setaf 3`
setaf_red=`tput setaf 4`
setaf_magenta=`tput setaf 5`
setaf_yellow=`tput setaf 6`
setaf_white=`tput setaf 7`

# For 88-colour rxvt-unicode these 8 colours are near-enough right.
# Try something like this to see the available colours:
#
# i=0
# while true
# do
#   tput setaf $i
#   echo "$i: Hello World"
#   i=$(($i+1))
# done

# Define the colours to use.
# Set prompt_colour to the escape sequence for the colour to use for
# the prompt, and cmd_colours (an array) to the escape sequences for
# the colours to use for the commands, in turn.

declare -a cmd_colours

# A complicating factor is that we don't know whether this terminal
# has a white / light-coloured background or a black / dark-coloured
# background.  This should influence our choice of colours.

# Here is a heuristic: virtual consoles have TERM=linux and are
# white-on-black, while X terminals and similar have TERM=xterm and
# are black-on-white.

case $TERM in
linux)   background=dark ;;
*xterm*) background=light ;;
*)       background=light ;;
esac

# Choose colours, based on the guessed background colour:

case $background in
dark)  prompt_colour=$setaf_white
       cmd_colours=($setaf_cyan $setaf_magenta $setaf_yellow) ;;
light) prompt_colour=$setaf_black
       cmd_colours=($setaf_blue $setaf_green $setaf_red) ;;
esac

# This variable tracks which command colour is currently in use:

declare -i cmd_colour_index=-1

# This function increments cmd_colour_index modulo the number of
# command colours:

function next_colour() {
  cmd_colour_index=$(( ($cmd_colour_index+1) % (${#cmd_colours[*]}) ))
}

# PROMPT_COMMAND is a bash builtin variable that names a command to
# run before each prompt.  (This will override any existing use of
# this variable; how can multiple commands be chained together?)

PROMPT_COMMAND=next_colour

# This function returns the escape sequence for the current command
# colour, from the array defined above:

function cmd_colour() {
  echo ${cmd_colours[$cmd_colour_index]}
}

# Define the prompt variables.  For each one we encapsulate the
# current setting between a prefix and a suffix; the prefix sets the
# colour for the prompt and the suffix sets the colour for the
# command.

# \[ ... \] are used around non-printing characters and are needed so
# that Bash correctly calculates the screen position.  Functions can
# be called using `...`, as long as the promptvars option is enabled:

shopt -s promptvars

prompt_prefix="\\[${prompt_colour}\\]"
prompt_suffix='\[`cmd_colour`\]'

PS1="${prompt_prefix}${PS1}${prompt_suffix}"
PS2="${prompt_prefix}${PS2}${prompt_suffix}"
PS3="${prompt_prefix}${PS3}${prompt_suffix}"
PS4="${prompt_prefix}${PS4}${prompt_suffix}"

