aboutsummaryrefslogtreecommitdiff
path: root/pdksh/pdkshrc.d/prompt.pdksh
blob: f9dfa584762d70067d6766d82ccb150d43dc4c92 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# All of this is only known to work on OpenBSD's fork of pdksh
[[ $(uname -s) == OpenBSD ]] || return

# Frontend to controlling prompt
prompt() {

    # If no arguments, print the prompt strings as they are
    if ! (($#)) ; then
        printf '%s\n' PS1="$PS1" PS2="$PS2" PS3="$PS3" PS4="$PS4"
        return
    fi

    # What's done next depends on the first argument to the function
    case $1 in

        # Turn complex, colored PS1 and debugging PS4 prompts on
        on)
            # Set up prompt, including optional PROMPT_PREFIX and PROMPT_SUFFIX
            # variables
            PS1='\u@\h:\w'
            PS1=$PS1'$(prompt vcs)'
            PS1=$PS1'$(prompt job)'
            PS1='${PROMPT_PREFIX}'$PS1
            PS1=$PS1'${PROMPT_SUFFIX}'
            PS1=$PS1'\$'

            # Count available colors
            typeset -i colors
            colors=$( {
                tput Co || tput colors
            } 2>/dev/null )

            # Prepare reset code
            typeset reset
            reset=$( {
                tput me || tput sgr0
            } 2>/dev/null )

            # Decide prompt color formatting based on color availability
            typeset format
            case $colors in

                # Check if we have non-bold bright green available
                256)
                    format=$( {
                        : "${PROMPT_COLOR:=12}"
                        tput AF "$PROMPT_COLOR" ||
                        tput setaf "$PROMPT_COLOR" ||
                        tput AF "$PROMPT_COLOR" 0 0  ||
                        tput setaf "$PROMPT_COLOR" 0 0
                    } 2>/dev/null )
                    ;;

                # If we have only eight colors, use bold green
                8)
                    format=$( {
                        : "${PROMPT_COLOR:=4}"
                        tput AF "$PROMPT_COLOR" ||
                        tput setaf "$PROMPT_COLOR"
                        tput md || tput bold
                    } 2>/dev/null )
                    ;;

                # For all other terminals, we assume non-color (!), and we just
                # use bold
                *)
                    format=$( {
                        tput md || tput bold
                    } 2>/dev/null )
                    ;;
            esac

            # String it all together
            PS1='\['"$format"'\]'"$PS1"'\['"$reset"'\] '
            PS2='> '
            PS3='? '
            PS4='+<$?> $LINENO:'
            ;;

        # Git prompt function
        git)
            # Bail if we have no git(1)
            if ! hash git 2>/dev/null ; then
                return 1
            fi

            # Attempt to determine git branch, bail if we can't
            typeset branch
            branch=$( {
                git symbolic-ref --quiet HEAD ||
                git rev-parse --short HEAD
            } 2>/dev/null )
            if [[ ! -n $branch ]] ; then
                return 1
            fi
            branch=${branch##*/}

            # Refresh index so e.g. git-diff-files(1) is accurate
            git update-index --refresh >/dev/null

            # Collect symbols representing repository state
            typeset state

            # Upstream HEAD has commits after local HEAD; we're "behind"
            (($(git rev-list --count 'HEAD..@{u}' 2>/dev/null) > 0)) &&
                state=${state}\<

            # Local HEAD has commits after upstream HEAD; we're "ahead"
            (($(git rev-list --count '@{u}..HEAD' 2>/dev/null) > 0)) &&
                state=${state}\>

            # Tracked files are modified
            git diff-files --quiet ||
                state=${state}'!!'

            # Changes are staged
            git diff-index --cached --quiet HEAD ||
                state=${state}\+

            # There are some untracked and unignored files
            [[ -n $(git ls-files --others --exclude-standard) ]] &&
                state=${state}\?

            # There are stashed changes
            git rev-parse --quiet --verify refs/stash >/dev/null &&
                state=${state}\^

            # Print the status in brackets; add a git: prefix only if there
            # might be another VCS prompt (because PROMPT_VCS is set)
            printf '(%s%s%s)' "${PROMPT_VCS:+git:}" "${branch:-unknown}" "$state"
            ;;

        # Revert to simple inexpensive prompts
        off)
            PS1='\$ '
            PS2='> '
            PS3='? '
            PS4='+ '
            ;;

        # VCS wrapper prompt function; print the first relevant prompt, if any
        vcs)
            typeset vcs
            for vcs in "${PROMPT_VCS[@]:-git}" ; do
                if prompt "$vcs" ; then
                    return
                fi
            done
            ;;

        # Show the count of background jobs in curly brackets, if not zero
        job)
            typeset -i jobc
            jobc=$(jobs -p | sed -n '$=')
            if ((jobc > 0)) ; then
                printf '{%u}' "$jobc"
            fi
            ;;

        # Print error
        *)
            printf 'prompt: Unknown command %s\n' "$1" >&2
            return 2
            ;;

    esac
}

# Start with full-fledged prompt
prompt on