Skip to content
Mark Vander Stel edited this page Oct 3, 2022 · 16 revisions

WIP

IFS

Always set a local IFS when doing something that references it. Never assume that the user or Liquidprompt has a sane IFS set.

If running a for loop on the output of a command, like

for temp in $(sensors -u); do
  ...

you should set a local beforehand like local IFS=$'\n'.

If using read to read data from a file or command, set IFS as part of the read command:

IFS= read var <myfile
IFS=" " read var1 var2 var3 <<<"1 2 3"

Quoting

When in doubt, always quote a shell variable ("$var").

Special shell variables like $? and $- are treated no differently by the shell, they must also be quoted.

There are two cases where quoting is optional:

  1. When setting a variable: var=$other_var. (But not when it comes after local or typeset, see below.) When setting a variable to something other than one variable, use quotes for clarity: var="${var1}${var2}".
  2. When referencing a variable as the first variable in a [[ ... ]] block.

Both of these exceptions are not exceptions if the string you are building contains whitespace:

var=$myvar $othervar    # does not work
var="$myvar $othervar"  # fixed

[[ $var $othervar == one two ]]      # does not work
[[ "$var $othervar" == "one two" ]]  # fixed

If comparing two variables in a [[ ... ]] block, the second variable must be quoted to prevent it being expanded as a glob:

[[ $var == $othervar ]]      # does not work, $othervar will be subjected to glob expansion
[[ $var == "$othervar" ]]    # fixed

In Zsh version 5.0.X (which Liquidprompt supports, and therefore must be accounted for), variable assignments that are part of a local or typeset command are treated as arguments, not assignments. They either must be quoted or on a separate line:

typeset var=$myvar  # not safe!
typeset var="$myvar"  # safe
typeset var
var=$myvar  # also safe
typeset var=()  # not safe!
typeset -a var
var=()  # safe

The typeset var="$myvar" form is preferred when declaring one variable, while the typeset var form is preferred when declaring more than one local on the same line.

There are almost no scenarios where quoting a variable is incorrect, so unless you know for sure that not quoting it is safe, just quote it!

Arrays

Bash versions 3.2 and 4.2 are both filled with bugs when it comes to arrays. See commit e121179 and this Stack Overflow answer for details, but in general:

Any time an array is referenced, unless there is 100% certainty that both the array will never be empty and none of the values in the array will be the empty string, instead of using the form "${array[@]}", the form ${array[@]+"${array[@]}"} must be used instead. When getting the number of values in the array (the length), unless you know the array is already set, doing array=( ${array[@]+"${array[@]}"} ) first (ideally in the default config section) is required.

To ensure portability between Bash (which start indexing at 0) and Zsh (which starts indexing at 1), you must use _LP_FIRST_INDEX when accessing an item in array, for instance, for accessing the 3rd element (thus at index 2 for Bash): ${array[_LP_FIRST_INDEX+2]}.

If declaring an array as a local variable, it must be done as local -a var, and the assignment must be on a following line due to a bug in Zsh 5.0.X.

Naming

Functions

Functions starting with lp or any other alphanumeric character are public functions designed to be used by users on the command line or in their config.

Functions starting with _lp are theme level functions, designed to be used by themes. These include data, theme, and utility functions.

Functions starting with __lp are internal functions, designed to be used only by Liquid Prompt internals. These functions should not be used by users or themes, as they are not guaranteed to not change between versions.

Return values

Theme and internal functions should return a code, thus any value computed by a function should go in a public variable. This variable should use the function name without the underscore prefix. For instance: the _lp_grep_fields function sets the lp_grep_fields variable with its "returned" value.

Data functions

Enabling/disabling

New data feature should generally come with a global variable controlling if the feature is enabled or not. The variable should be named LP_ENABLE_X.

Purpose

Functions adding new data to the prompt should be separated in two:

  1. a function for computing the data itself —without formatting—, it generally starts with a (( LP_ENABLE_X )) || return 2 optimization guard,
  2. a function for formatting the computed data, named with a _color suffix, that should be guarded with _lp_<data_function> || return 2.

Return value

All data functions will return "true" (meaning return code 0) when they are both enabled and have data. They will return "false" (meaning return code 1) when they do not have data. Most will return 2 when they are disabled, either through the config or because the tool they depend on is not installed. Such disable config options will be documented. Exceptions to this rule are explicitly documented.

When a function returns "false", any return variables are not guaranteed to be set. If running with set -u (or when building a theme to be distributed), guard any return variable accesses with a check of the return code, or use ${var-} syntax.

Zsh special variables

Zsh has a handful of variables that are "special", and do not play well with being declared as local. Always avoid naming any variables these (IFS being the exception, see above):

% for p in $parameters[(I)*]; do print $p $parameters[$p]; done | grep special | sort
$ integer-readonly-special
0 scalar-special
aliases association-hide-hideval-special
ARGC integer-readonly-special
argv array-special
* array-readonly-special
@ array-readonly-special
builtins association-readonly-hide-hideval-special
cdpath array-special
CDPATH scalar-special
COLUMNS integer-special
commands association-hide-hideval-special
dirstack array-hide-hideval-special
dis_aliases association-hide-hideval-special
dis_builtins association-readonly-hide-hideval-special
dis_functions association-hide-hideval-special
dis_galiases association-hide-hideval-special
dis_patchars array-readonly-hide-hideval-special
dis_reswords array-readonly-hide-hideval-special
dis_saliases association-hide-hideval-special
EGID integer-special
EUID integer-special
fignore array-special
FIGNORE scalar-special
fpath array-special
FPATH scalar-special
funcfiletrace array-readonly-hide-hideval-special
FUNCNEST integer-special
funcsourcetrace array-readonly-hide-hideval-special
funcstack array-readonly-hide-hideval-special
functions association-hide-hideval-special
functrace array-readonly-hide-hideval-special
galiases association-hide-hideval-special
GID integer-special
histchars scalar-special
HISTCHARS scalar-special
HISTCMD integer-readonly-special
history association-readonly-hide-hideval-special
historywords array-readonly-hide-hideval-special
HISTSIZE integer-export-special
HOME scalar-export-special
IFS scalar-special
! integer-readonly-special
# integer-readonly-special
? integer-readonly-special
jobdirs association-readonly-hide-hideval-special
jobstates association-readonly-hide-hideval-special
jobtexts association-readonly-hide-hideval-special
KEYBOARD_HACK scalar-special
keymaps array-readonly-hide-hideval-special
LANG scalar-export-special
LINENO integer-readonly-special
LINES integer-special
mailpath array-special
MAILPATH scalar-special
manpath array-special
MANPATH scalar-special
module_path array-special
MODULE_PATH scalar-special
modules association-readonly-hide-hideval-special
nameddirs association-hide-hideval-special
NULLCMD scalar-special
OPTARG scalar-special
OPTIND integer-special
options association-hide-hideval-special
parameters association-readonly-hide-hideval-special
patchars array-readonly-hide-hideval-special
path array-special
PATH scalar-export-special
pipestatus array-special
PPID integer-readonly-special
PROMPT2 scalar-special
PROMPT3 scalar-special
PROMPT4 scalar-special
prompt scalar-special
PROMPT scalar-special
PS1 scalar-export-special
PS2 scalar-special
PS3 scalar-special
PS4 scalar-special
psvar array-special
PSVAR scalar-special
RANDOM integer-special
READNULLCMD scalar-special
reswords array-readonly-hide-hideval-special
saliases association-hide-hideval-special
SAVEHIST integer-special
scalar-readonly-special
_ scalar-special
SECONDS integer-special
SHLVL integer-export-special
SPROMPT scalar-special
status integer-readonly-special
termcap association-readonly-hide-hideval-special
terminfo association-readonly-hide-hideval-special
TERM scalar-export-special
TRY_BLOCK_ERROR integer-special
TRY_BLOCK_INTERRUPT integer-special
TTYIDLE integer-readonly-special
UID integer-special
userdirs association-readonly-hide-hideval-special
USERNAME scalar-special
watch array-special
WATCH scalar-special
widgets association-readonly-hide-hideval-special
WORDCHARS scalar-special
zsh_eval_context array-readonly-special
ZSH_EVAL_CONTEXT scalar-readonly-special
zsh_scheduled_events array-readonly-hide-hideval-special
ZSH_SUBSHELL integer-readonly-special