liquidprompt 56 KB
Newer Older
1

2
3
################################################################################
# LIQUID PROMPT
4
# An intelligent and non intrusive prompt for bash and zsh
5
6
################################################################################

nojhan's avatar
nojhan committed
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# Licensed under the AGPL version 3
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

23
24
25
26
###########
# AUTHORS #
###########

Anthony Gelibert's avatar
Anthony Gelibert committed
27
28
# Alex Prengère     <alexprengere@gmail.com>      # Untracked git files
# Anthony Gelibert  <anthony.gelibert@me.com>     # Several fix
nojhan's avatar
nojhan committed
29
# Aurelien Requiem  <aurelien@requiem.fr>         # Major clean refactoring, variable path length, error codes, several bugfixes.
Anthony Gelibert's avatar
Anthony Gelibert committed
30
# Brendan Fahy      <bmfahy@gmail.com>            # Postfix variable
nojhan's avatar
nojhan committed
31
# Clément Mathieu   <clement@unportant.info>      # Bazaar support
Anthony Gelibert's avatar
Anthony Gelibert committed
32
# David Loureiro    <david.loureiro@sysfera.com>  # Small portability fix
33
# Étienne Deparis   <etienne@depar.is>            # Fossil support
nojhan's avatar
nojhan committed
34
# Florian Le Frioux <florian@lefrioux.fr>         # Use ± mark when root in VCS dir.
Anthony Gelibert's avatar
Anthony Gelibert committed
35
# François Schmidts <francois.schmidts@gmail.com> # Small code fix, _lp_get_dirtrim
nojhan's avatar
nojhan committed
36
37
38
# Frédéric Lepied   <flepied@gmail.com>           # Python virtual env
# Jonas Bengtsson   <jonas.b@gmail.com>           # Git remotes fix
# Joris Dedieu      <joris@pontiac3.nfrance.com>  # Portability framework, FreeBSD support, bugfixes.
Anthony Gelibert's avatar
Anthony Gelibert committed
39
40
# Joris Vaillant    <joris.vaillant@gmail.com>    # Small git fix
# Luc Didry         <luc@fiat-tux.fr>             # ZSH port, several fix
nojhan's avatar
nojhan committed
41
# Ludovic Rousseau  <ludovic.rousseau@gmail.com>  # Lot of bugfixes.
Markus Dreseler's avatar
Markus Dreseler committed
42
# Markus Dreseler   <github@dreseler.de>          # Runtime of last command
Anthony Gelibert's avatar
Anthony Gelibert committed
43
# Nicolas Lacourte  <nicolas@dotinfra.fr>         # Screen title
44
45
# nojhan            <nojhan@gmail.com>            # Original author.
# Olivier Mengué    <dolmen@cpan.org>             # Major optimizations, refactorings everywhere; current maintainer
Anthony Gelibert's avatar
Anthony Gelibert committed
46
# Poil              <poil@quake.fr>               # Speed improvements
nojhan's avatar
nojhan committed
47
48
# Thomas Debesse    <thomas.debesse@gmail.com>    # Fix columns use.
# Yann 'Ze' Richard <ze@nbox.org>                 # Do not fail on missing commands.
49
50
51

# See the README.md file for a summary of features.

Olivier Mengué's avatar
Olivier Mengué committed
52
53
# Check for recent enough version of bash.
if test -n "$BASH_VERSION" -a -n "$PS1" -a -n "$TERM" ; then
54
55
56
57
58
    bash=${BASH_VERSION%.*}; bmajor=${bash%.*}; bminor=${bash#*.}
    if [[ $bmajor -lt 3 ]] || [[ $bmajor -eq 3 && $bminor -lt 2 ]]; then
        unset bash bmajor bminor
        return
    fi
nojhan's avatar
nojhan committed
59
    unset bash bmajor bminor
60

Olivier Mengué's avatar
Olivier Mengué committed
61
62
    _LP_SHELL_bash=true
    _LP_SHELL_zsh=false
63
    _LP_OPEN_ESC="\["
64
    _LP_CLOSE_ESC="\]"
65
66
    _LP_USER_SYMBOL="\u"
    _LP_HOST_SYMBOL="\h"
67
    _LP_TIME_SYMBOL="\t"
Olivier Mengué's avatar
Olivier Mengué committed
68
    _LP_MARK_SYMBOL='\$'
69
    _LP_FIRST_INDEX=0
Olivier Mengué's avatar
Olivier Mengué committed
70
elif test -n "$ZSH_VERSION" ; then
Olivier Mengué's avatar
Olivier Mengué committed
71
72
    _LP_SHELL_bash=false
    _LP_SHELL_zsh=true
73
    _LP_OPEN_ESC="%{"
74
    _LP_CLOSE_ESC="%}"
75
76
    _LP_USER_SYMBOL="%n"
    _LP_HOST_SYMBOL="%m"
77
    _LP_TIME_SYMBOL="%*"
Olivier Mengué's avatar
Olivier Mengué committed
78
    _LP_MARK_SYMBOL='%(!.#.%%)'
79
    _LP_FIRST_INDEX=1
Olivier Mengué's avatar
Olivier Mengué committed
80
else
nojhan's avatar
nojhan committed
81
82
    echo "liquidprompt: shell not supported" >&2
    return
83
84
fi

nojhan's avatar
nojhan committed
85
86

###############
nojhan's avatar
nojhan committed
87
# OS specific #
nojhan's avatar
nojhan committed
88
89
###############

90
# LP_OS detection, default to Linux
91
case $(uname) in
Olivier Mengué's avatar
Olivier Mengué committed
92
93
    FreeBSD)   LP_OS=FreeBSD ;;
    DragonFly) LP_OS=FreeBSD ;;
Kim Lidström's avatar
Kim Lidström committed
94
    OpenBSD)   LP_OS=OpenBSD ;;
Ludovic Rousseau's avatar
Ludovic Rousseau committed
95
    Darwin)    LP_OS=Darwin  ;;
Olivier Mengué's avatar
Olivier Mengué committed
96
97
    SunOS)     LP_OS=SunOS   ;;
    *)         LP_OS=Linux   ;;
98
esac
99

Olivier Mengué's avatar
Olivier Mengué committed
100
101
# Get cpu count
case "$LP_OS" in
102
    Linux)   _lp_CPUNUM=$( nproc 2>/dev/null || grep -c '^[Pp]rocessor' /proc/cpuinfo ) ;;
Kim Lidström's avatar
Kim Lidström committed
103
    FreeBSD|Darwin|OpenBSD) _lp_CPUNUM=$( sysctl -n hw.ncpu ) ;;
Olivier Mengué's avatar
Olivier Mengué committed
104
105
    SunOS)   _lp_CPUNUM=$( kstat -m cpu_info | grep -c "module: cpu_info" ) ;;
esac
106
107
108


# get current load
Olivier Mengué's avatar
Olivier Mengué committed
109
110
111
112
113
114
115
116
case "$LP_OS" in
    Linux)
        _lp_cpu_load () {
            local load eol
            read load eol < /proc/loadavg
            echo "$load"
        }
        ;;
Ludovic Rousseau's avatar
Ludovic Rousseau committed
117
    FreeBSD)
Olivier Mengué's avatar
Olivier Mengué committed
118
119
        _lp_cpu_load () {
            local bol load eol
120
121
122
123
            # If you have problems with syntax coloring due to the following
            # line, do this: ln -s liquidprompt liquidprompt.bash
            # and edit liquidprompt.bash
            read bol load eol <<<$( LANG=C sysctl -n vm.loadavg )
Olivier Mengué's avatar
Olivier Mengué committed
124
125
126
            echo "$load"
        }
        ;;
Ludovic Rousseau's avatar
Ludovic Rousseau committed
127
128
    Darwin)
        _lp_cpu_load () {
129
130
            local bol load eol
            read bol load eol <<<$( LANG=C sysctl -n vm.loadavg )
Ludovic Rousseau's avatar
Ludovic Rousseau committed
131
132
            echo "$load"
        }
133
        LP_DWIN_KERNEL_REL_VER=$(uname -r | cut -d . -f 1)
Ludovic Rousseau's avatar
Ludovic Rousseau committed
134
        ;;
Kim Lidström's avatar
Kim Lidström committed
135
136
    OpenBSD)
        _lp_cpu_load() {
137
138
139
            local bol load eol
            read one two three <<< `sysctl -n vm.loadavg`
            echo "$load"
Kim Lidström's avatar
Kim Lidström committed
140
141
        }
        ;;
Olivier Mengué's avatar
Olivier Mengué committed
142
143
144
145
146
    SunOS)
        _lp_cpu_load () {
            LANG=C uptime | awk '{print substr($10,0,length($10))}'
        }
esac
147

148

nojhan's avatar
nojhan committed
149
150
151
152
#################
# CONFIGURATION #
#################

Olivier Mengué's avatar
Olivier Mengué committed
153
154
155
156
# The following code is run just once. But it is encapsulated in a function
# to benefit of 'local' variables.
#
# What we do here:
Olivier Mengué's avatar
Olivier Mengué committed
157
158
159
160
161
# 1. Setup variables that can be used by the user: the "API" of liquidprompt
#    for config/theme. Those variables are local to the function.
#    In practice, this is only color variables.
# 2. Setup default values
# 3. Load the configuration
nojhan's avatar
nojhan committed
162
163
_lp_source_config()
{
164

165
    # TermInfo feature detection
166
167
168
    local ti_sgr0="$( { tput sgr0 || tput me ; } 2>/dev/null )"
    local ti_bold="$( { tput bold || tput md ; } 2>/dev/null )"
    local ti_setaf
169
    if tput setaf >/dev/null 2>&1 ; then
170
        ti_setaf () { tput setaf "$1" ; }
171
172
    elif tput AF >/dev/null 2>&1 ; then
        # *BSD
173
        ti_setaf () { tput AF "$1" ; }
Kim Lidström's avatar
Kim Lidström committed
174
175
176
    elif tput AF 1 >/dev/null 2>&1 ; then
        # OpenBSD
        ti_setaf () { tput AF "$1" ; }
177
178
    else
        echo "liquidprompt: terminal $TERM not supported" >&2
179
        ti_setaf () { : ; }
180
    fi
181

182
183
184
    # Colors: variables are local so they will have a value only
    # during config loading and will not conflict with other values
    # with the same names defined by the user outside the config.
185
    local BOLD="${_LP_OPEN_ESC}${ti_bold}${_LP_CLOSE_ESC}"
186

187
188
189
190
    local BLACK="${_LP_OPEN_ESC}$(ti_setaf 0)${_LP_CLOSE_ESC}"
    local BOLD_GRAY="${_LP_OPEN_ESC}${ti_bold}$(ti_setaf 0)${_LP_CLOSE_ESC}"
    local WHITE="${_LP_OPEN_ESC}$(ti_setaf 7)${_LP_CLOSE_ESC}"
    local BOLD_WHITE="${_LP_OPEN_ESC}${ti_bold}$(ti_setaf 7)${_LP_CLOSE_ESC}"
191

192
193
194
195
196
    local RED="${_LP_OPEN_ESC}$(ti_setaf 1)${_LP_CLOSE_ESC}"
    local BOLD_RED="${_LP_OPEN_ESC}${ti_bold}$(ti_setaf 1)${_LP_CLOSE_ESC}"
    local WARN_RED="${_LP_OPEN_ESC}$(ti_setaf 0 ; tput setab 1)${_LP_CLOSE_ESC}"
    local CRIT_RED="${_LP_OPEN_ESC}${ti_bold}$(ti_setaf 7 ; tput setab 1)${_LP_CLOSE_ESC}"
    local DANGER_RED="${_LP_OPEN_ESC}${ti_bold}$(ti_setaf 3 ; tput setab 1)${_LP_CLOSE_ESC}"
197

198
199
    local GREEN="${_LP_OPEN_ESC}$(ti_setaf 2)${_LP_CLOSE_ESC}"
    local BOLD_GREEN="${_LP_OPEN_ESC}${ti_bold}$(ti_setaf 2)${_LP_CLOSE_ESC}"
200

201
202
    local YELLOW="${_LP_OPEN_ESC}$(ti_setaf 3)${_LP_CLOSE_ESC}"
    local BOLD_YELLOW="${_LP_OPEN_ESC}${ti_bold}$(ti_setaf 3)${_LP_CLOSE_ESC}"
203

204
205
    local BLUE="${_LP_OPEN_ESC}$(ti_setaf 4)${_LP_CLOSE_ESC}"
    local BOLD_BLUE="${_LP_OPEN_ESC}${ti_bold}$(ti_setaf 4)${_LP_CLOSE_ESC}"
206

207
208
    local PURPLE="${_LP_OPEN_ESC}$(ti_setaf 5)${_LP_CLOSE_ESC}"
    local PINK="${_LP_OPEN_ESC}${ti_bold}$(ti_setaf 5)${_LP_CLOSE_ESC}"
209

210
211
    local CYAN="${_LP_OPEN_ESC}$(ti_setaf 6)${_LP_CLOSE_ESC}"
    local BOLD_CYAN="${_LP_OPEN_ESC}${ti_bold}$(ti_setaf 6)${_LP_CLOSE_ESC}"
212

213
    # NO_COL is special: it will be used at runtime, not just during config loading
214
    NO_COL="${_LP_OPEN_ESC}${ti_sgr0}${_LP_CLOSE_ESC}"
215

216
    unset ti_sgr0 ti_bold ti_setaf
217

218

Olivier Mengué's avatar
Olivier Mengué committed
219
    # Default values (globals)
220
221
    LP_BATTERY_THRESHOLD=${LP_BATTERY_THRESHOLD:-75}
    LP_LOAD_THRESHOLD=${LP_LOAD_THRESHOLD:-60}
222
    LP_TEMP_THRESHOLD=${LP_TEMP_THRESHOLD:-60}
Markus Dreseler's avatar
Markus Dreseler committed
223
    LP_RUNTIME_THRESHOLD=${LP_RUNTIME_THRESHOLD:-2}
224
225
226
    LP_PATH_LENGTH=${LP_PATH_LENGTH:-35}
    LP_PATH_KEEP=${LP_PATH_KEEP:-2}
    LP_HOSTNAME_ALWAYS=${LP_HOSTNAME_ALWAYS:-0}
227
    LP_USER_ALWAYS=${LP_USER_ALWAYS:-1}
228
    LP_PERCENTS_ALWAYS=${LP_PERCENTS_ALWAYS:-1}
229
    LP_PS1=${LP_PS1:-""}
nojhan's avatar
nojhan committed
230
    LP_PS1_PREFIX=${LP_PS1_PREFIX:-""}
Brendan Fahy's avatar
Brendan Fahy committed
231
    LP_PS1_POSTFIX=${LP_PS1_POSTFIX:-""}
nojhan's avatar
nojhan committed
232
233
    LP_TITLE_OPEN=${LP_TITLE_OPEN:-"\e]0;"}
    LP_TITLE_CLOSE=${LP_TITLE_CLOSE:-"\a"}
234
235
    LP_SCREEN_TITLE_OPEN=${LP_SCREEN_TITLE_OPEN:-"\033k"}
    LP_SCREEN_TITLE_CLOSE=${LP_SCREEN_TITLE_CLOSE:-"\033\134"}
236
237
238
239

    LP_ENABLE_PERM=${LP_ENABLE_PERM:-1}
    LP_ENABLE_SHORTEN_PATH=${LP_ENABLE_SHORTEN_PATH:-1}
    LP_ENABLE_PROXY=${LP_ENABLE_PROXY:-1}
240
    LP_ENABLE_TEMP=${LP_ENABLE_TEMP:-1}
241
242
243
244
245
    LP_ENABLE_JOBS=${LP_ENABLE_JOBS:-1}
    LP_ENABLE_LOAD=${LP_ENABLE_LOAD:-1}
    LP_ENABLE_BATT=${LP_ENABLE_BATT:-1}
    LP_ENABLE_GIT=${LP_ENABLE_GIT:-1}
    LP_ENABLE_SVN=${LP_ENABLE_SVN:-1}
Étienne Deparis's avatar
Étienne Deparis committed
246
    LP_ENABLE_FOSSIL=${LP_ENABLE_FOSSIL:-1}
247
    LP_ENABLE_HG=${LP_ENABLE_HG:-1}
Clément MATHIEU's avatar
Clément MATHIEU committed
248
    LP_ENABLE_BZR=${LP_ENABLE_BZR:-1}
249
    LP_ENABLE_TIME=${LP_ENABLE_TIME:-0}
Markus Dreseler's avatar
Markus Dreseler committed
250
251
252
253
254
255
256
    if [[ "$_LP_SHELL_bash" == true ]]
    then
        LP_ENABLE_RUNTIME=${LP_ENABLE_RUNTIME:-1}
    else
        [[ "$LP_ENABLE_RUNTIME" == 1 ]] && echo Unfortunately, runtime printing for zsh is not yet supported. Turn LP_ENABLE_RUNTIME off in your config to hide this message.
        LP_ENABLE_RUNTIME=0
    fi
257
    LP_ENABLE_VIRTUALENV=${LP_ENABLE_VIRTUALENV:-1}
258
    LP_ENABLE_VCS_ROOT=${LP_ENABLE_VCS_ROOT:-0}
nojhan's avatar
nojhan committed
259
    LP_ENABLE_TITLE=${LP_ENABLE_TITLE:-0}
260
    LP_ENABLE_SCREEN_TITLE=${LP_ENABLE_SCREEN_TITLE:-0}
261
    LP_ENABLE_SSH_COLORS=${LP_ENABLE_SSH_COLORS:-0}
262
    LP_DISABLED_VCS_PATH=${LP_DISABLED_VCS_PATH:-""}
263

264
    LP_MARK_DEFAULT=${LP_MARK_DEFAULT:-""}
265
266
267
    LP_MARK_BATTERY=${LP_MARK_BATTERY:-"⌁"}
    LP_MARK_ADAPTER=${LP_MARK_ADAPTER:-"⏚"}
    LP_MARK_LOAD=${LP_MARK_LOAD:-"⌂"}
268
    LP_MARK_TEMP=${LP_MARK_TEMP:-"θ"}
269
270
271
272
    LP_MARK_PROXY=${LP_MARK_PROXY:-"↥"}
    LP_MARK_HG=${LP_MARK_HG:-"☿"}
    LP_MARK_SVN=${LP_MARK_SVN:-"‡"}
    LP_MARK_GIT=${LP_MARK_GIT:-"±"}
Étienne Deparis's avatar
Étienne Deparis committed
273
    LP_MARK_FOSSIL=${LP_MARK_FOSSIL:-"⌘"}
Clément MATHIEU's avatar
Clément MATHIEU committed
274
    LP_MARK_BZR=${LP_MARK_BZR:-"⚯"}
275
    LP_MARK_DISABLED=${LP_MARK_DISABLED:-"⌀"}
276
    LP_MARK_UNTRACKED=${LP_MARK_UNTRACKED:-"*"}
nojhan's avatar
nojhan committed
277
    LP_MARK_STASH=${LP_MARK_STASH:-"+"}
278
279
    LP_MARK_BRACKET_OPEN=${LP_MARK_BRACKET_OPEN:-"["}
    LP_MARK_BRACKET_CLOSE=${LP_MARK_BRACKET_CLOSE:-"]"}
280
    LP_MARK_SHORTEN_PATH=${LP_MARK_SHORTEN_PATH:-" … "}
281

282
    LP_COLOR_PATH=${LP_COLOR_PATH:-$BOLD}
283
284
285
286
287
288
    LP_COLOR_PATH_ROOT=${LP_COLOR_PATH_ROOT:-$BOLD_YELLOW}
    LP_COLOR_PROXY=${LP_COLOR_PROXY:-$BOLD_BLUE}
    LP_COLOR_JOB_D=${LP_COLOR_JOB_D:-$YELLOW}
    LP_COLOR_JOB_R=${LP_COLOR_JOB_R:-$BOLD_YELLOW}
    LP_COLOR_JOB_Z=${LP_COLOR_JOB_Z:-$BOLD_YELLOW}
    LP_COLOR_ERR=${LP_COLOR_ERR:-$PURPLE}
289
    LP_COLOR_MARK=${LP_COLOR_MARK:-$BOLD}
290
291
292
293
294
    LP_COLOR_MARK_ROOT=${LP_COLOR_MARK_ROOT:-$BOLD_RED}
    LP_COLOR_USER_LOGGED=${LP_COLOR_USER_LOGGED:-""}
    LP_COLOR_USER_ALT=${LP_COLOR_USER_ALT:-$BOLD}
    LP_COLOR_USER_ROOT=${_ROOT:-$BOLD_YELLOW}
    LP_COLOR_HOST=${LP_COLOR_HOST:-""}
nojhan's avatar
nojhan committed
295
    LP_COLOR_SSH=${LP_COLOR_SSH:-$BLUE}
296
    LP_COLOR_SU=${LP_COLOR_SU:-$BOLD_YELLOW}
297
    LP_COLOR_TELNET=${LP_COLOR_TELNET:-$WARN_RED}
nojhan's avatar
nojhan committed
298
299
    LP_COLOR_X11_ON=${LP_COLOR_X11:-$GREEN}
    LP_COLOR_X11_OFF=${LP_COLOR_X11:-$YELLOW}
300
301
302
303
304
305
306
307
308
309
310
    LP_COLOR_WRITE=${LP_COLOR_WRITE:-$GREEN}
    LP_COLOR_NOWRITE=${LP_COLOR_NOWRITE:-$RED}
    LP_COLOR_UP=${LP_COLOR_UP:-$GREEN}
    LP_COLOR_COMMITS=${LP_COLOR_COMMITS:-$YELLOW}
    LP_COLOR_CHANGES=${LP_COLOR_CHANGES:-$RED}
    LP_COLOR_DIFF=${LP_COLOR_DIFF:-$PURPLE}
    LP_COLOR_CHARGING_ABOVE=${LP_COLOR_CHARGING_ABOVE:-$GREEN}
    LP_COLOR_CHARGING_UNDER=${LP_COLOR_CHARGING_UNDER:-$YELLOW}
    LP_COLOR_DISCHARGING_ABOVE=${LP_COLOR_DISCHARGING_ABOVE:-$YELLOW}
    LP_COLOR_DISCHARGING_UNDER=${LP_COLOR_DISCHARGING_UNDER:-$RED}
    LP_COLOR_TIME=${LP_COLOR_TIME:-$BLUE}
311
    LP_COLOR_IN_MULTIPLEXER=${LP_COLOR_IN_MULTIPLEXER:-$BOLD_BLUE}
Markus Dreseler's avatar
Markus Dreseler committed
312
    LP_COLOR_RUNTIME=${LP_COLOR_RUNTIME:-$YELLOW}
313
314
315
316
317
318
319
320
321
322
323

    LP_COLORMAP_0=${LP_COLORMAP_0:-""}
    LP_COLORMAP_1=${LP_COLORMAP_1:-$GREEN}
    LP_COLORMAP_2=${LP_COLORMAP_2:-$BOLD_GREEN}
    LP_COLORMAP_3=${LP_COLORMAP_3:-$YELLOW}
    LP_COLORMAP_4=${LP_COLORMAP_4:-$BOLD_YELLOW}
    LP_COLORMAP_5=${LP_COLORMAP_5:-$RED}
    LP_COLORMAP_6=${LP_COLORMAP_6:-$BOLD_RED}
    LP_COLORMAP_7=${LP_COLORMAP_7:-$WARN_RED}
    LP_COLORMAP_8=${LP_COLORMAP_8:-$CRIT_RED}
    LP_COLORMAP_9=${LP_COLORMAP_9:-$DANGER_RED}
nojhan's avatar
nojhan committed
324

325
326
    # Debugging flags
    LP_DEBUG_TIME=${LP_DEBUG_TIME:-0}
nojhan's avatar
nojhan committed
327

328
329
330
    # Default config file may be the XDG standard ~/.config/liquidpromptrc,
    # but heirloom dotfile has priority.

nojhan's avatar
nojhan committed
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
    local configfile
    if [[ -f "/etc/liquidpromptrc" ]]
    then
        source "/etc/liquidpromptrc"
    fi
    if [[ -f "$HOME/.liquidpromptrc" ]]
    then
        configfile="$HOME/.liquidpromptrc"
    elif [[ -z "$XDG_HOME_DIR" ]]
    then
        configfile="$HOME/.config/liquidpromptrc"
    else
        configfile="$XDG_HOME_DIR/liquidpromptrc"
    fi
    if [[ -f "$configfile" ]]
    then
        source "$configfile"
    fi
}
# do source config files
_lp_source_config
352
unset _lp_source_config
nojhan's avatar
nojhan committed
353

354
# Disable features if the tool is not installed
355
356
[[ "$LP_ENABLE_GIT"  = 1 ]] && { command -v git  >/dev/null || LP_ENABLE_GIT=0  ; }
[[ "$LP_ENABLE_SVN"  = 1 ]] && { command -v svn  >/dev/null || LP_ENABLE_SVN=0  ; }
Étienne Deparis's avatar
Étienne Deparis committed
357
[[ "$LP_ENABLE_FOSSIL"  = 1 ]] && { command -v fossil  >/dev/null || LP_ENABLE_FOSSIL=0  ; }
358
[[ "$LP_ENABLE_HG"   = 1 ]] && { command -v hg   >/dev/null || LP_ENABLE_HG=0   ; }
Clément MATHIEU's avatar
Clément MATHIEU committed
359
[[ "$LP_ENABLE_BZR"  = 1 ]] && { command -v bzr > /dev/null || LP_ENABLE_BZR=0  ; }
George Macon's avatar
George Macon committed
360
case "$LP_OS" in
361
    Darwin) [[ "$LP_ENABLE_BATT" = 1 ]] && { command -v pmset >/dev/null || LP_ENABLE_BATT=0 ; };;
George Macon's avatar
George Macon committed
362
363
    *)      [[ "$LP_ENABLE_BATT" = 1 ]] && { command -v acpi >/dev/null || LP_ENABLE_BATT=0 ; };;
esac
364

365
366
# If we are running in a terminal multiplexer, brackets are colored
if [[ "$TERM" == screen* ]]; then
367
368
369
370
371
    LP_BRACKET_OPEN="${LP_COLOR_IN_MULTIPLEXER}${LP_MARK_BRACKET_OPEN}${NO_COL}"
    LP_BRACKET_CLOSE="${LP_COLOR_IN_MULTIPLEXER}${LP_MARK_BRACKET_CLOSE}${NO_COL}"
else
    LP_BRACKET_OPEN="${LP_MARK_BRACKET_OPEN}"
    LP_BRACKET_CLOSE="${LP_MARK_BRACKET_CLOSE}"
372
373
fi

374
# Escape the given strings
375
# Must be used for all strings that may comes from remote sources,
376
377
378
379
380
# like VCS branch names
_lp_escape()
{
    printf "%q" "$*"
}
381

nojhan's avatar
nojhan committed
382

383
384
385
386
###############
# Who are we? #
###############

387
# Yellow for root, bold if the user is not the login one, else no color.
388
389
if [[ "$EUID" -ne "0" ]] ; then  # if user is not root
    # if user is not login user
390
    if [[ ${USER} != "$(logname 2>/dev/null || echo $LOGNAME)" ]]; then
391
392
393
394
        LP_USER="${LP_COLOR_USER_ALT}${_LP_USER_SYMBOL}${NO_COL}"
    else
        if [[ "${LP_USER_ALWAYS}" -ne "0" ]] ; then
            LP_USER="${LP_COLOR_USER_LOGGED}${_LP_USER_SYMBOL}${NO_COL}"
395
        else
396
            LP_USER=""
397
398
        fi
    fi
399
else # root!
400
    LP_USER="${LP_COLOR_USER_ROOT}${_LP_USER_SYMBOL}${NO_COL}"
401
402
    LP_COLOR_MARK="${LP_COLOR_MARK_ROOT}"
    LP_COLOR_PATH="${LP_COLOR_PATH_ROOT}"
403
    # Disable VCS info for all paths
Olivier Mengué's avatar
Olivier Mengué committed
404
405
406
407
    if [[ "$LP_ENABLE_VCS_ROOT" != 1 ]]; then
        LP_DISABLED_VCS_PATH=/
        LP_MARK_DISABLED="$_LP_MARK_SYMBOL"
    fi
408
fi
409
410


411
412
413
414
#################
# Where are we? #
#################

415
_lp_connection()
416
{
417
    if [[ -n "$SSH_CLIENT$SSH2_CLIENT$SSH_TTY" ]] ; then
Olivier Mengué's avatar
Olivier Mengué committed
418
        echo ssh
419
    else
Olivier Mengué's avatar
Olivier Mengué committed
420
        # TODO check on *BSD
421
422
        local sess_src="$(who am i | sed -n 's/.*(\(.*\))/\1/p')"
        local sess_parent="$(ps -o comm= -p $PPID 2> /dev/null)"
423
        if [[ -z "$sess_src" || "$sess_src" = ":"* ]] ; then
Olivier Mengué's avatar
Olivier Mengué committed
424
            echo lcl  # Local
425
426
        elif [[ "$sess_parent" = "su" || "$sess_parent" = "sudo" ]] ; then
            echo su   # Remote su/sudo
Olivier Mengué's avatar
Olivier Mengué committed
427
428
429
        else
            echo tel  # Telnet
        fi
430
431
432
433
434
435
    fi
}

# Put the hostname if not locally connected
# color it in cyan within SSH, and a warning red if within telnet
# else diplay the host without color
Olivier Mengué's avatar
Olivier Mengué committed
436
437
# The connection is not expected to change from inside the shell, so we
# build this just once
438
LP_HOST=""
Ludovic Rousseau's avatar
Ludovic Rousseau committed
439
440
441
442
_chroot()
{
    if [[ -r /etc/debian_chroot ]] ; then
        local debchroot
443
        debchroot="$(cat /etc/debian_chroot)"
Ludovic Rousseau's avatar
Ludovic Rousseau committed
444
445
446
447
448
        echo "(${debchroot})"
    fi
}
LP_HOST="$(_chroot)"
unset _chroot
449

450
451
# If we are connected with a X11 support
if [[ -n "$DISPLAY" ]] ; then
nojhan's avatar
nojhan committed
452
    LP_HOST="${LP_COLOR_X11_ON}${LP_HOST}@${NO_COL}"
453
else
nojhan's avatar
nojhan committed
454
    LP_HOST="${LP_COLOR_X11_OFF}${LP_HOST}@${NO_COL}"
455
456
fi

Olivier Mengué's avatar
Olivier Mengué committed
457
458
case "$(_lp_connection)" in
lcl)
459
    if [[ "${LP_HOSTNAME_ALWAYS}" -eq "0" ]] ; then
460
        # FIXME do we want to display the chroot if local?
461
        LP_HOST="" # no hostname if local
462
    else
463
        LP_HOST="${LP_HOST}${LP_COLOR_HOST}${_LP_HOST_SYMBOL}${NO_COL}"
464
    fi
Olivier Mengué's avatar
Olivier Mengué committed
465
    ;;
Olivier Mengué's avatar
Olivier Mengué committed
466
ssh)
467
    # If we want a different color for each host
468
    if [[ "$LP_ENABLE_SSH_COLORS" -eq "1" ]]; then
469
470
471
472
473
474
475
476
477
478
479
480
        # compute the hash of the hostname
        # and get the corresponding number in [1-6] (red,green,yellow,blue,purple or cyan)
        # FIXME check portability of cksum and add more formats (bold? 256 colors?)
        hash=$(( 1 + $(hostname | cksum | cut -d " " -f 1) % 6 ))
        color=${_LP_OPEN_ESC}$(ti_setaf $hash)${_LP_CLOSE_ESC}
        LP_HOST="${LP_HOST}${color}${_LP_HOST_SYMBOL}${NO_COL}"
        unset hash
        unset color
    else
        # the same color for all hosts
        LP_HOST="${LP_HOST}${LP_COLOR_SSH}${_LP_HOST_SYMBOL}${NO_COL}"
    fi
Olivier Mengué's avatar
Olivier Mengué committed
481
    ;;
482
483
su)
    LP_HOST="${LP_HOST}${LP_COLOR_SU}${_LP_HOST_SYMBOL}${NO_COL}"
Olivier Mengué's avatar
Olivier Mengué committed
484
    ;;
Olivier Mengué's avatar
Olivier Mengué committed
485
tel)
486
    LP_HOST="${LP_HOST}${LP_COLOR_TELNET}${_LP_HOST_SYMBOL}${NO_COL}"
Olivier Mengué's avatar
Olivier Mengué committed
487
488
    ;;
*)
489
    LP_HOST="${LP_HOST}${_LP_HOST_SYMBOL}" # defaults to no color
Olivier Mengué's avatar
Olivier Mengué committed
490
491
    ;;
esac
492

Olivier Mengué's avatar
Olivier Mengué committed
493
# Useless now, so undefine
494
unset _lp_connection
495
496


497
# BASH/ZSH function that shortens
498
499
500
501
502
503
504
505
506
507
508
# a very long path for display by removing
# the left most parts and replacing them
# with a leading ...
#
# the first argument is the path
#
# the second argument is the maximum allowed
# length including the '/'s and ...
# http://hbfs.wordpress.com/2009/09/01/short-pwd-in-bash-prompts/
#
# + keep some left part of the path if asked
509
_lp_shorten_path()
510
{
511
    if [[ "$LP_ENABLE_SHORTEN_PATH" != 1 || -n "$PROMPT_DIRTRIM" ]] ; then
Olivier Mengué's avatar
Olivier Mengué committed
512
        if $_LP_SHELL_bash; then
513
514
            echo "\\w"
        else
515
            print -P '%~'
516
        fi
517
518
        return
    fi
519

Cougar's avatar
Cougar committed
520
    local p="${PWD/#$HOME/~}"
521
522
    local -i len=${#p}
    local -i max_len=$((${COLUMNS:-80}*$LP_PATH_LENGTH/100))
523

Olivier Mengué's avatar
Olivier Mengué committed
524
    if $_LP_SHELL_bash; then
525
        if (( len > max_len ))
Luc Didry's avatar
Luc Didry committed
526
        then
527
528
529
530
531
532
533
            # index of the directory to keep from the root
            # (starts at 0 whith bash, 1 with zsh)
            local -i keep=LP_PATH_KEEP-1
            # the character that will replace the part of the path that is
            # masked
            local mask="$LP_MARK_SHORTEN_PATH"
            local -i mask_len=${#mask}
Luc Didry's avatar
Luc Didry committed
534
535
536
537
            # finds all the '/' in
            # the path and stores their
            # positions
            #
538
            local pos=()
539
            local -i slashes=0
540
541
            for ((i=0;i<len;i++))
            do
542
                if [[ "${p:i:1}" == / ]]
543
544
                then
                    pos=(${pos[@]} $i)
545
                    let slashes++
546
547
548
549
550
551
552
553
                fi
            done
            pos=(${pos[@]} $len)

            # we have the '/'s, let's find the
            # left-most that doesn't break the
            # length limit
            #
554
555
556
            local -i i=keep
            if (( keep > slashes )) ; then
                i=slashes
557
            fi
558
            while (( len-pos[i] > max_len-mask_len ))
559
            do
560
                let i++
561
            done
562

563
564
            # let us check if it's OK to
            # print the whole thing
565
            #
566
            if (( pos[i] == 0 ))
567
568
569
            then
                # the path is shorter than
                # the maximum allowed length,
570
                # so no need for '…'
571
572
                #
                echo "$p"
573

574
            elif (( pos[i] == len ))
575
576
577
578
579
580
            then
                # constraints are broken because
                # the maximum allowed size is smaller
                # than the last part of the path, plus
                # ' … '
                #
581
                echo "${p:0:pos[keep]+1}${mask}${p:len-max_len+mask_len}"
582
583
584
585
586
            else
                # constraints are satisfied, at least
                # some parts of the path, plus ' … ', are
                # shorter than the maximum allowed size
                #
587
                echo "${p:0:pos[keep]+1}${mask}${p:pos[i]}"
588
            fi
Luc Didry's avatar
Luc Didry committed
589
590
        else
            echo "$p"
591
        fi
Olivier Mengué's avatar
Olivier Mengué committed
592
    else # zsh
593
        unset p
594
        if (( len > max_len )); then
595
            print -P "%-${LP_PATH_KEEP}~%${max_len}<${LP_MARK_SHORTEN_PATH}<%~%<<"
Luc Didry's avatar
Luc Didry committed
596
        else
597
            print -P '%~'
Luc Didry's avatar
Luc Didry committed
598
        fi
599
600
601
    fi
}

602
603
604
605
# In bash shell, PROMPT_DIRTRIM is the number of directory to keep at the end
# of the displayed path (if "\w" is present in the PS1 var).
# liquidprompt can calculate this number under two condition, path shortening
# must be activated and PROMPT_DIRTRIM must be already set.
606
_lp_get_dirtrim() {
607
    [[ "$LP_ENABLE_SHORTEN_PATH" != 1 ]] && echo 0 && return
608

609
610
    local p="${PWD/$HOME/~}"
    local len=${#p}
611
612
613
    local max_len=$((${COLUMNS:-80}*$LP_PATH_LENGTH/100))
    local PROMPT_DIRTRIM=0

614
    if [[ "$((len))" -gt "$((max_len))" ]]; then
615
616
        local i

617
        for ((i=$len;i>=0;i--))
618
        do
619
620
            [[ $(($len-$i)) -gt $max_len ]] && break
            [[ "${p:i:1}" == "/" ]] && PROMPT_DIRTRIM=$((PROMPT_DIRTRIM+1))
621
622
623
        done
        [[ "$((PROMPT_DIRTRIM))" -eq 0 ]] && PROMPT_DIRTRIM=1
    fi
624
    unset p
625
626
627
    echo "$PROMPT_DIRTRIM"
}

628
629
630
# Display a ":"
# colored in green if user have write permission on the current directory
# colored in red if it have not.
631
_lp_permissions_color()
632
{
633
634
    if [[ "$LP_ENABLE_PERM" != 1 ]]; then
        echo : # without color
635
    else
636
637
638
639
640
        if [[ -w "${PWD}" ]]; then
            echo "${LP_COLOR_WRITE}:${NO_COL}"
        else
            echo "${LP_COLOR_NOWRITE}:${NO_COL}"
        fi
641
642
643
    fi
}

644
645
646
647
# Display the current Python virtual environnement, if available.
_lp_virtualenv()
{
    [[ "$LP_ENABLE_VIRTUALENV" != 1 ]] && return
648
649
650
    if [[ -n "$VIRTUAL_ENV" ]]; then
        echo "[${LP_COLOR_VIRTUALENV}$(basename $VIRTUAL_ENV)${NO_COL}]"
    fi
651
652
}

nojhan's avatar
nojhan committed
653

nojhan's avatar
nojhan committed
654
655
656
################
# Related jobs #
################
657

658
659
660
661
# Display the count of each if non-zero:
# - detached screens sessions and/or tmux sessions running on the host
# - attached running jobs (started with $ myjob &)
# - attached stopped jobs (suspended with Ctrl-Z)
662
_lp_jobcount_color()
663
{
664
665
    [[ "$LP_ENABLE_JOBS" != 1 ]] && return

Poil's avatar
Poil committed
666
667
    local running=$(( $(jobs -r | wc -l) ))
    local stopped=$(( $(jobs -s | wc -l) ))
668
    local n_screen=$(screen -ls 2> /dev/null | grep -c Detach)
669
    local n_tmux=$(tmux list-sessions 2> /dev/null | grep -cv attached)
670
    local detached=$(( $n_screen + $n_tmux ))
671
672
673
    local m_detached="d"
    local m_stop="z"
    local m_run="&"
674
    local ret=""
nojhan's avatar
nojhan committed
675

676
677
678
    if [[ $detached != "0" ]] ; then
        ret="${ret}${LP_COLOR_JOB_D}${detached}${m_detached}${NO_COL}"
    fi
nojhan's avatar
nojhan committed
679

680
681
682
683
    if [[ $running != "0" ]] ; then
        if [[ $ret != "" ]] ; then ret="${ret}/"; fi
        ret="${ret}${LP_COLOR_JOB_R}${running}${m_run}${NO_COL}"
    fi
684

685
686
687
    if [[ $stopped != "0" ]] ; then
        if [[ $ret != "" ]] ; then ret="${ret}/"; fi
        ret="${ret}${LP_COLOR_JOB_Z}${stopped}${m_stop}${NO_COL}"
nojhan's avatar
nojhan committed
688
    fi
689
690

    echo -ne "$ret"
691
692
}

693

nojhan's avatar
nojhan committed
694

695
######################
nojhan's avatar
nojhan committed
696
# VCS branch display #
697
698
######################

699
_lp_are_vcs_enabled()
700
{
701
    [[ -z "$LP_DISABLED_VCS_PATH" ]] && return 0
702
703
704
    local path
    local IFS=:
    for path in $LP_DISABLED_VCS_PATH; do
705
        [[ "$PWD" == *"$path"* ]] && return 1
706
    done
707
    return 0
708
709
}

nojhan's avatar
nojhan committed
710
711
# GIT #

712
# Get the branch name of the current directory
713
_lp_git_branch()
714
{
715
    [[ "$LP_ENABLE_GIT" != 1 ]] && return
716
    local gitdir
717
    gitdir="$([ $(\git ls-files . 2>/dev/null | wc -l) -gt 0 ] && \git rev-parse --git-dir 2>/dev/null)"
718
    [[ $? -ne 0 || ! $gitdir =~ (.*\/)?\.git.* ]] && return
719
720
721
722
723
724
    local branch
    # Recent versions of Git support the --short option for symbolic-ref, but
    # not 1.7.9 (Ubuntu 12.04)
    if branch="$(\git symbolic-ref -q HEAD)"; then
        _lp_escape "${branch#refs/heads/}"
    else
725
        # In detached head state, use commit instead
726
        # No escape needed
727
        \git rev-parse --short -q HEAD
728
    fi
729
730
731
732
733
734
}

# Set a color depending on the branch state:
# - green if the repository is up to date
# - yellow if there is some commits not pushed
# - red if there is changes to commit
735
736
#
# Add the number of pending commits and the impacted lines.
737
_lp_git_branch_color()
738
{
739
740
    [[ "$LP_ENABLE_GIT" != 1 ]] && return

741
    local branch
742
    branch="$(_lp_git_branch)"
Olivier Mengué's avatar
Olivier Mengué committed
743
    if [[ -n "$branch" ]] ; then
nojhan's avatar
nojhan committed
744

745
746
        local end
        end="$NO_COL"
747
        if LC_ALL=C \git status 2>/dev/null | grep -q '\(# Untracked\)'; then
748
            end="$LP_COLOR_CHANGES$LP_MARK_UNTRACKED$end"
749
750
        fi

751
        if [[ -n "$(\git stash list 2>/dev/null)" ]]; then
752
            end="$LP_COLOR_COMMITS$LP_MARK_STASH$end"
nojhan's avatar
nojhan committed
753
754
        fi

755
        local remote
756
        remote="$(\git config --get branch.${branch}.remote 2>/dev/null)"
757

758
        local has_commit
759
        has_commit=0
760
761
        if [[ -n "$remote" ]] ; then
            local remote_branch
762
            remote_branch="$(\git config --get branch.${branch}.merge)"
763
            if [[ -n "$remote_branch" ]] ; then
764
                has_commit="$(\git rev-list --no-merges --count ${remote_branch/refs\/heads/refs\/remotes\/$remote}..HEAD 2>/dev/null)"
765
766
767
                if [[ -z "$has_commit" ]] ; then
                    has_commit=0
                fi
768
            fi
nojhan's avatar
nojhan committed
769
        fi
770

771
        local shortstat
772
        shortstat="$(LC_ALL=C \git diff --shortstat HEAD 2>/dev/null)"
773
        if [[ -n "$shortstat" ]] ; then
774
            local has_lines
775
            #has_lines=$(\git diff --numstat 2>/dev/null | awk 'NF==3 {plus+=$1; minus+=$2} END {printf("+%d/-%d\n", plus, minus)}')
776
            has_lines=${shortstat/*changed, /}
777
            has_lines=${has_lines/ inser*, /\/-}
778
            has_lines=${has_lines/ del*/}
779
            [[ "$shortstat" = *insertion* ]] && has_lines="+${has_lines/ inser*/}" || has_lines="-$has_lines"
780

781
            if [[ "$has_commit" -gt "0" ]] ; then
782
                # Changes to commit and commits to push
783
                ret="${LP_COLOR_CHANGES}${branch}${NO_COL}(${LP_COLOR_DIFF}$has_lines${NO_COL},${LP_COLOR_COMMITS}$has_commit${NO_COL})"
nojhan's avatar
nojhan committed
784
            else
785
                ret="${LP_COLOR_CHANGES}${branch}${NO_COL}(${LP_COLOR_DIFF}$has_lines${NO_COL})" # changes to commit
nojhan's avatar
nojhan committed
786
787
            fi
        else
788
            if [[ "$has_commit" -gt "0" ]] ; then
nojhan's avatar
nojhan committed
789
                # some commit(s) to push
790
                ret="${LP_COLOR_COMMITS}${branch}${NO_COL}(${LP_COLOR_COMMITS}$has_commit${NO_COL})"
nojhan's avatar
nojhan committed
791
            else
792
                ret="${LP_COLOR_UP}${branch}" # nothing to commit or push
nojhan's avatar
nojhan committed
793
794
            fi
        fi
795
        echo -ne "$ret$end"
nojhan's avatar
nojhan committed
796
    fi
797
798
}

799
800
# Search upwards through a directory structure looking for a file/folder with
# the given name.  Used to avoid invoking 'hg' and 'bzr'.
801
802
_lp_upwards_find()
{
803
804
805
806
807
808
809
810
811
812
813
    while [[ "$PWD" != "/" ]] ; do
        
        # See if it's in the current directory.
        local found=$(find "$PWD"/ -maxdepth 1 -name "$@" 2> /dev/null)
        
        # If it is, then echo the path (or anything), and return.
        if [[ -n found ]] ; then
            echo $found
            return
        fi
        
814
815
816
817
        cd ..
    done
}

nojhan's avatar
nojhan committed
818
819
820
# MERCURIAL #

# Get the branch name of the current directory
821
_lp_hg_branch()
nojhan's avatar
nojhan committed
822
{
823
    [[ "$LP_ENABLE_HG" != 1 ]] && return
824
825
826
827
   
    # First do a simple search to avoid having to invoke hg -- at least on my
    # machine, the python startup causes a noticeable hitch when changing
    # directories.
828
    [[ -z "$(_lp_upwards_find '.hg')" ]] && return
829
830
831
832

    # We found an .hg folder, so we need to invoke hg and see if we're actually
    # in a repository.
    
833
    local branch
nojhan's avatar
nojhan committed
834
    branch="$(hg branch 2>/dev/null)"
835

836
    [[ $? -eq 0 ]] && _lp_escape "$branch"
nojhan's avatar
nojhan committed
837
838
839
840
841
842
}

# Set a color depending on the branch state:
# - green if the repository is up to date
# - red if there is changes to commit
# - TODO: yellow if there is some commits not pushed
843
_lp_hg_branch_color()
nojhan's avatar
nojhan committed
844
{
845
846
    [[ "$LP_ENABLE_HG" != 1 ]] && return

847
848
    local branch
    local ret
849
    branch="$(_lp_hg_branch)"
Olivier Mengué's avatar
Olivier Mengué committed
850
    if [[ -n "$branch" ]] ; then
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865

        local has_untracked
        has_untracked=$(hg status 2>/dev/null | grep '\(^\?\)' | wc -l)
        if [[ -z "$has_untracked" ]] ; then
            has_untracked=""
        else
            has_untracked="$LP_COLOR_CHANGES$LP_MARK_UNTRACKED"
        fi

        local has_commit
        has_commit=$(hg outgoing --no-merges ${branch} 2>/dev/null | grep '\(^changeset\:\)' | wc -l)
        if [[ -z "$has_commit" ]] ; then
            has_commit=0
        fi

Poil's avatar
Poil committed
866
        if [[ $(( $(hg status --quiet -n | wc -l) )) = 0 ]] ; then
867
868
869
870
871
872
            if [[ "$has_commit" -gt "0" ]] ; then
                # some commit(s) to push
                ret="${LP_COLOR_COMMITS}${branch}${NO_COL}(${LP_COLOR_COMMITS}$has_commit${NO_COL})${has_untracked}${NO_COL}"
            else
                ret="