From 22845bf10e8c2bafbf41f32368e9a4c80a00d262 Mon Sep 17 00:00:00 2001
From: Joshua Randall <jranda10@emich.edu>
Date: Wed, 29 Jan 2025 12:05:05 -0500
Subject: [PATCH] added headline.zsh-theme

---
 headline.zsh-theme | 702 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 702 insertions(+)
 create mode 100644 headline.zsh-theme

diff --git a/headline.zsh-theme b/headline.zsh-theme
new file mode 100644
index 0000000..9a4722f
--- /dev/null
+++ b/headline.zsh-theme
@@ -0,0 +1,702 @@
+#!/bin/zsh
+
+# Headline ZSH Prompt
+# Copyright (c) 2024 Moarram under the MIT License
+
+# To install, source this file from your ~/.zshrc
+# Customization variables begin around line 80
+
+
+# Formatting aliases
+# (add more if you need)
+reset=$'\e[0m'
+bold=$'\e[1m'
+faint=$'\e[2m';     no_faint_bold=$'\e[22m'
+italic=$'\e[3m';    no_italic=$'\e[23m'
+underline=$'\e[4m'; no_underline=$'\e[24m'
+invert=$'\e[7m';    no_invert=$'\e[27m'
+# ...
+
+# Foreground color aliases
+black=$'\e[30m'
+red=$'\e[31m'
+green=$'\e[32m'
+yellow=$'\e[33m'
+blue=$'\e[34m'
+magenta=$'\e[35m'
+cyan=$'\e[36m'
+white=$'\e[37m'
+light_black=$'\e[90m'
+light_red=$'\e[91m'
+light_green=$'\e[92m'
+light_yellow=$'\e[93m'
+light_blue=$'\e[94m'
+light_magenta=$'\e[95m'
+light_cyan=$'\e[96m'
+light_white=$'\e[97m'
+default_fg=$'\e[39m'
+
+# Background color aliases
+black_bg=$'\e[40m'
+red_bg=$'\e[41m'
+green_bg=$'\e[42m'
+yellow_bg=$'\e[43m'
+blue_bg=$'\e[44m'
+magenta_bg=$'\e[45m'
+cyan_bg=$'\e[46m'
+white_bg=$'\e[47m'
+light_black_bg=$'\e[100m'
+light_red_bg=$'\e[101m'
+light_green_bg=$'\e[102m'
+light_yellow_bg=$'\e[103m'
+light_blue_bg=$'\e[104m'
+light_magenta_bg=$'\e[105m'
+light_cyan_bg=$'\e[106m'
+light_white_bg=$'\e[107m'
+default_bg=$'\e[49m'
+
+# Custom colors
+# Ref: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters
+# orange_yellow=$'\e[38;5;214m' # example 8-bit color (n=214)
+# orange_brown=$'\e[38;2;191;116;46m' # example rgb color (r=119, g=116, b=46)
+# ...
+
+# Terminal control aliases
+cursor_up=$'\e[1F'
+cursor_show=$'\e[?25h'
+cursor_hide=$'\e[?25l'
+cursor_to_top_left_corner=$'\e[H'
+clear_entire_screen=$'\e[2J'
+# ...
+
+# Flags
+[ ! -z "$SSH_TTY$SSH_CONNECTION$SSH_CLIENT" ] && IS_SSH='true'
+
+
+
+# ------------------------------------------------------------------------------
+# Customization
+# Use the following variables to customize the theme.
+# If you're setting them in ~/.zshrc, source the theme, THEN set the variables.
+# To insert styles (ANSI SGR codes defined above) use syntax: "%{$style%}"
+
+
+# Print separator and information line with precmd hook or in PROMPT
+HL_PRINT_MODE='precmd' # precmd|prompt
+
+# Print the separator line always, when not following clear screen, or don't print
+HL_SEP_MODE='on' # on|auto|off
+
+# Print the information line always, when it has changed, or don't print
+HL_INFO_MODE='on' # on|auto|off
+
+# Press <enter> with no commands to overwrite previous prompt
+HL_OVERWRITE='off' # on|off
+
+
+# Style applied to separator line, after other styles
+HL_SEP_STYLE="%{$default_bg%}"
+
+# Segments of the separator line
+declare -A HL_SEP=(
+  _PRE  ''
+  _LINE '▁' # repeated char to create separator line, consider '▁'
+  _POST ''
+)
+
+
+# Style applied to all segments, before other styles
+HL_BASE_STYLE=""
+
+# Style of segment layout template
+HL_LAYOUT_STYLE="%{$faint%}"
+
+# Order of segments
+declare -a HL_LAYOUT_ORDER=(
+  _PRE USER HOST CONDA VENV PATH _SPACER BRANCH STATUS _POST # ...
+)
+
+# Template for each segment's layout
+declare -A HL_LAYOUT_TEMPLATE=(
+  _PRE    "${IS_SSH+=> }" # shows "=> " if this is an ssh session
+  USER    '...'
+  HOST    ' @ ...'
+  CONDA   ' (...)'
+  VENV    ' (...)'
+  PATH    ': ...'
+  _SPACER ' | ' # special, only shows when compact, otherwise fill with space
+  BRANCH  '...'
+  STATUS  ' [...]'
+  _POST   ''
+  # ...
+)
+
+# Template for first segment's layout (when prior segments removed)
+declare -A HL_LAYOUT_FIRST=(
+  CONDA   '(...)' 
+  VENV    '(...)'
+  PATH    '...'
+  _SPACER ''
+  # ...
+)
+
+# The character used by _SPACER segment to fill space
+HL_SPACE_CHAR=' '
+
+
+# Template for each segment's content
+declare -A HL_CONTENT_TEMPLATE=(
+  USER   "%{$bold$red%} ..." # consider ' ' or ' '
+  HOST   "%{$bold$yellow%}󰇅..." # consider '󰇅 ' or ' '
+  CONDA   "%{$bold$green%} ..." # consider ' ' or ' '
+  VENV   "%{$bold$green%} ..." # consider ' ' or ' '
+  PATH   "%{$bold$blue%}î—¾ ..." # consider 'î—¾ ' or 'î«· '
+  BRANCH "%{$bold$cyan%} ..." # consider ' ' or ' '
+  STATUS "%{$bold$magenta%}..."
+  # ...
+)
+
+# Commands to produce each segment's content
+declare -A HL_CONTENT_SOURCE=(
+  USER   'echo $USER'
+  HOST   'hostname -s'
+  CONDA  'echo $CONDA_DEFAULT_ENV'
+  VENV   'basename "$VIRTUAL_ENV"'
+  PATH   'print -rP "%~"'
+  BRANCH 'headline-git-branch'
+  STATUS 'headline-git-status'
+  # ...
+)
+
+
+# Show count of each status always, only when greater than one, or don't show
+HL_GIT_COUNT_MODE='off' # on|auto|off
+
+# Symbol to join each status
+HL_GIT_SEP_SYMBOL=''
+
+# Order of statuses
+declare -a HL_GIT_STATUS_ORDER=(
+  STAGED CHANGED UNTRACKED BEHIND AHEAD DIVERGED STASHED CONFLICTS CLEAN
+)
+
+# Symbol for each status
+declare -A HL_GIT_STATUS_SYMBOLS=(
+  STAGED    '+'
+  CHANGED   '!'
+  UNTRACKED '?'
+  BEHIND    '↓'
+  AHEAD     '↑'
+  DIVERGED  '↕'
+  STASHED   '*'
+  CONFLICTS '✘' # consider "%{$red%}✘"
+  CLEAN     '' # consider '✓' or "%{$green%}✔"
+)
+
+
+# Minimum screen width to show segment
+declare -A HL_COLS_REMOVAL=(
+  USER   50
+  HOST   70
+  CONDA  30
+  VENV   30
+  # ...
+)
+
+# Order to truncate & remove segments
+declare -a HL_TRUNC_ORDER=(
+  HOST USER CONDA VENV PATH BRANCH # ...
+)
+
+# Symbol to insert when truncating a segment
+HL_TRUNC_SYMBOL='...' # consider '…'
+
+# Minimum segment length for initial truncation
+HL_TRUNC_INITIAL=16
+
+# Minimum segment length before removal
+HL_TRUNC_REMOVAL=2
+
+
+# Prompt
+HL_PROMPT='%(#.#.%(!.!.$)) ' # consider '%#'
+
+# Right prompt
+HL_RPROMPT=''
+
+
+# Show the clock, or don't show
+HL_CLOCK_MODE='off' # on|off
+
+# Template for the clock
+HL_CLOCK_TEMPLATE="%{$faint%}..."
+
+# Command which outputs clock content
+HL_CLOCK_SOURCE='date "+%l:%M:%S %p"' # consider 'date +%+' for full date
+
+
+# Show non-zero exit code, include a guessed meaning too, or don't show
+HL_ERR_MODE='off' # on|detail|off
+
+# Template for the exit code
+HL_ERR_TEMPLATE="%{$faint$italic%}→ ..."
+
+# Template for the optional detail
+HL_ERR_DETAIL_TEMPLATE=' (...)'
+
+
+# The string to replace in templates
+HL_TEMPLATE_TOKEN='...'
+
+# ------------------------------------------------------------------------------
+
+
+
+# Output variables
+HL_OUTPUT_SEP='' # printed separator line
+HL_OUTPUT_INFO='' # printed information line
+
+# Local variables
+_HL_SEP='' # computed separator line
+_HL_INFO='' # computed information line
+_HL_AT_TOP='true' # whether prompt is at top of the screen
+_HL_CMD_NUM=0 # number of commands entered
+_HL_CMD_NUM_PREV=-1 # previous number of commands entered, no command if same
+
+# Zsh configuration
+setopt PROMPT_SP # always start prompt on new line
+setopt PROMPT_SUBST # enable substitutions
+autoload -U add-zsh-hook
+PROMPT_EOL_MARK='' # remove weird % symbol
+ZLE_RPROMPT_INDENT=0 # remove extra space
+
+
+# Calculate length of string, excluding formatting characters
+headline-prompt-len() { # (str, num?)
+  # Ref: https://old.reddit.com/r/zsh/comments/cgbm24/multiline_prompt_the_missing_ingredient/
+  emulate -L zsh
+  local -i COLUMNS=${2:-COLUMNS}
+  local -i x y=${#1} m
+  if (( y )); then
+    while (( ${${(%):-$1%$y(l.1.0)}[-1]} )); do
+      x=y
+      (( y *= 2 ))
+    done
+    while (( y > x + 1 )); do
+      (( m = x + (y - x) / 2 ))
+      (( ${${(%):-$1%$m(l.x.y)}[-1]} = m ))
+    done
+  fi
+  echo $x
+}
+
+# Repeat character a number of times
+headline-repeat-char() { # (char, num)
+  # Note: This replaces the "${(pl:$num::$char:)}" expansion
+  local result=''
+  for (( i = 0; i < $2; i++ )); do
+    result+=$1
+  done
+  echo $result
+}
+
+# Guess the exit code meaning
+headline-exit-meaning() { # (num)
+  # Ref: https://tldp.org/LDP/abs/html/exitcodes.html
+  # Ref: https://man7.org/linux/man-pages/man7/signal.7.html
+  # Note: These meanings are not standardized
+  case $1 in
+    126) echo 'Command cannot execute';;
+    127) echo 'Command not found';;
+    129) echo 'Hangup';;
+    130) echo 'Interrupted';;
+    131) echo 'Quit';;
+    132) echo 'Illegal instruction';;
+    133) echo 'Trapped';;
+    134) echo 'Aborted';;
+    135) echo 'Bus error';;
+    136) echo 'Arithmetic error';;
+    137) echo 'Killed';;
+    138) echo 'User signal 1';;
+    139) echo 'Segmentation fault';;
+    140) echo 'User signal 2';;
+    141) echo 'Pipe error';;
+    142) echo 'Alarm';;
+    143) echo 'Terminated';;
+    *) ;;
+  esac
+}
+
+# Git command wrapper
+headline-git() {
+  # TODO is this necessary?
+  GIT_OPTIONAL_LOCKS=0 command git "$@"
+}
+
+# Get git branch (or hash)
+headline-git-branch() {
+  local ref
+  ref=$(headline-git symbolic-ref --quiet HEAD 2> /dev/null)
+  local err=$?
+  if [[ $err == 0 ]]; then
+    echo ${ref#refs/heads/} # remove "refs/heads/" to get branch
+  else # not on a branch
+    [[ $err == 128 ]] && return  # not a git repo
+    ref=$(headline-git rev-parse --short HEAD 2> /dev/null) || return
+    echo ":${ref}" # hash prefixed to distingush from branch
+  fi
+}
+
+# Get the quantity of each git status
+headline-git-status-counts() {
+  local -A counts=(
+    'STAGED' 0 # staged changes
+    'CHANGED' 0 # unstaged changes
+    'UNTRACKED' 0 # untracked files
+    'BEHIND' 0 # commits behind
+    'AHEAD' 0 # commits ahead
+    'DIVERGED' 0 # commits diverged
+    'STASHED' 0 # stashed files
+    'CONFLICTS' 0 # conflicts
+    'CLEAN' 1 # clean branch 1=true 0=false
+  )
+
+  # Retrieve status
+  local raw lines
+  raw="$(headline-git status --porcelain -b 2> /dev/null)"
+  if [[ $? == 128 ]]; then
+    return 1 # catastrophic failure, abort
+  fi
+  lines=(${(@f)raw})
+
+  # Process tracking line
+  if [[ ${lines[1]} =~ '^## [^ ]+ \[(.*)\]' ]]; then
+    local items=("${(@s/,/)match}")
+    for item in $items; do
+      if [[ $item =~ '(behind|ahead|diverged) ([0-9]+)?' ]]; then
+        case $match[1] in
+          'behind') counts[BEHIND]=$match[2];;
+          'ahead') counts[AHEAD]=$match[2];;
+          'diverged') counts[DIVERGED]=$match[2];;
+        esac
+      fi
+    done
+  fi
+
+  # Process status lines
+  for line in $lines; do
+    if [[ $line =~ '^##|^!!' ]]; then
+      continue
+    elif [[ $line =~ '^U[ADU]|^[AD]U|^AA|^DD' ]]; then
+      counts[CONFLICTS]=$(( ${counts[CONFLICTS]} + 1 ))
+    elif [[ $line =~ '^\?\?' ]]; then
+      counts[UNTRACKED]=$(( ${counts[UNTRACKED]} + 1 ))
+    elif [[ $line =~ '^[MTADRC] ' ]]; then
+      counts[STAGED]=$(( ${counts[STAGED]} + 1 ))
+    elif [[ $line =~ '^[MTARC][MTD]' ]]; then
+      counts[STAGED]=$(( ${counts[STAGED]} + 1 ))
+      counts[CHANGED]=$(( ${counts[CHANGED]} + 1 ))
+    elif [[ $line =~ '^ [MTADRC]' ]]; then
+      counts[CHANGED]=$(( ${counts[CHANGED]} + 1 ))
+    fi
+  done
+
+  # Check for stashes
+  if $(headline-git rev-parse --verify refs/stash &> /dev/null); then
+    counts[STASHED]=$(headline-git rev-list --walk-reflogs --count refs/stash 2> /dev/null)
+  fi
+
+  # Update clean flag
+  for key val in ${(@kv)counts}; do
+    [[ $key == 'CLEAN' ]] && continue
+    (( $val > 0 )) && counts[CLEAN]=0
+  done
+
+  echo ${(@kv)counts} # key1 val1 key2 val2 ...
+}
+
+# Get git status
+headline-git-status() {
+  local parts=( ${(ps:$HL_TEMPLATE_TOKEN:)HL_CONTENT_TEMPLATE[STATUS]} ) # split on template token
+  local style=${${parts[1]##*%\{}%%%\}*} # regex for "%{...%}"
+  local -A counts=( $(headline-git-status-counts) )
+  (( ${#counts} == 0 )) && return # not a git repo
+  local result=''
+  for key in $HL_GIT_STATUS_ORDER; do
+    if (( ${counts[$key]} > 0 )); then
+      if (( ${#HL_GIT_SEP_SYMBOL} != 0 && ${#result} != 0 )); then
+        result+="%{$reset%}$HL_BASE_STYLE$HL_LAYOUT_STYLE$HL_GIT_SEP_SYMBOL%{$reset%}$HL_BASE_STYLE%{$style%}"
+      fi
+      if [[ $key != 'CLEAN' && $HL_GIT_COUNT_MODE == 'on' || ( $HL_GIT_COUNT_MODE == 'auto' && ${counts[$key]} != 1 ) ]]; then
+        result+="${counts[$key]}${HL_GIT_STATUS_SYMBOLS[$key]}"
+      else
+        result+="${HL_GIT_STATUS_SYMBOLS[$key]}"
+      fi
+    fi
+  done
+  echo $result
+}
+
+# Transfer styles to another string
+headline-transfer-styles() { # (str, str)
+  local -a src=( ${(@s::)1} ) # source char array
+  local -a dest=( ${(@s::)2} ) # destination char array
+  local result=''
+  local prev=''
+  local is_style='false'
+  local index=0
+  for char in $src; do
+    if [[ $prev == '{' || $prev == '}' ]]; then
+      prev=$char
+      continue
+    elif [[ $prev == '%' && $char == '{' ]]; then
+      [[ $is_style != 'true' ]] && result+='%{'
+      is_style='true'
+    elif [[ $prev == '%' && $char == '}' ]]; then
+      [[ $is_style == 'true' ]] && result+='%}'
+      is_style='false'
+    elif [[ $is_style == 'true' ]]; then
+      result+=$prev
+    else
+      result+=${dest[$index]}
+      (( index += 1 ))
+    fi
+    prev=$char
+  done
+  result+=${dest[$index]}
+  echo $result
+
+  # TODO use regex... why does this suck so much? can't match multiple?
+  # if [[ $1 =~ '%{([^%]*)%}' ]]; then
+  #   echo $MBEGIN $MEND $MATCH
+  #   echo $mbegin $mend $match # expect arrays?
+  # fi
+}
+
+
+# Handle Ctrl+L press
+zle -N headline-clear-screen
+bindkey '^L' headline-clear-screen
+headline-clear-screen() {
+  _HL_AT_TOP='true'
+  _HL_INFO='' # ensure info line will print
+
+  # Hide cursor and clear screen
+  print -nr "$cursor_hide$cursor_to_top_left_corner$clear_entire_screen"
+
+  # Update and print
+  for function in $precmd_functions; do
+    $function
+  done
+  zle .reset-prompt # re-print $PROMPT and $RPROMPT
+
+  # Show cursor
+  print -nr "$cursor_show"
+}
+
+# Before executing command
+add-zsh-hook preexec headline-preexec
+headline-preexec() {
+  (( _HL_CMD_NUM++ ))
+  # TODO better way of knowing the prompt is at the top of the terminal ?
+  if [[ $2 == 'clear' ]]; then
+    _HL_AT_TOP='true'
+    _HL_INFO='' # ensure info line will print
+  fi
+}
+
+# Before prompting
+add-zsh-hook precmd headline-precmd
+headline-precmd() {
+  local -i err=$?
+  local -i trunc_initial_length=$(( $HL_TRUNC_INITIAL + ${#HL_TRUNC_SYMBOL} ))
+  local -i trunc_removal_length=$(( $HL_TRUNC_REMOVAL + ${#HL_TRUNC_SYMBOL} ))
+
+  # Acquire contents
+  local -A contents
+  local -A content_lengths # length of each content (without style)
+  local -i content_length=0 # total length of content
+  for key val in "${(@kv)HL_CONTENT_SOURCE}"; do
+    content_lengths[$key]=0
+    (( $COLUMNS < ${HL_COLS_REMOVAL[$key]:-0} )) && continue # omit segment
+    contents[$key]=$(eval ${=val})
+    local -i length=$(headline-prompt-len ${contents[$key]:-''} 999)
+    (( content_length += $length )); content_lengths[$key]=$length
+  done
+
+  # Compute layout lengths
+  local -A layouts
+  local -A layout_lengths # length of each layout (without style)
+  local -i layout_length=0 # total length of layout
+  local -A first_layout_lengths # length of each first layout (without style)
+  for key val in "${(@kv)HL_LAYOUT_TEMPLATE}"; do
+    layout_lengths[$key]=0
+    local -i length=$(headline-prompt-len "$val$HL_CONTENT_TEMPLATE[$key]" 999)
+    local -i first_length=$(headline-prompt-len "$HL_LAYOUT_FIRST[$key]$HL_CONTENT_TEMPLATE[$key]" 999)
+    if [[ ${key[1]} != '_' ]]; then
+      (( content_lengths[$key] <= 0 )) && continue # skip omitted segment
+      (( length -= ${#HL_TEMPLATE_TOKEN} * 2 )) # subtract length of tokens
+      (( first_length -= ${#HL_TEMPLATE_TOKEN} * 2 )) # subtract length of tokens
+    fi
+    layouts[$key]=$val
+    (( layout_length += $length )); layout_lengths[$key]=$length
+    (( ${+HL_LAYOUT_FIRST[$key]} )) && first_layout_lengths[$key]=$first_length
+  done
+
+  # Compute target truncation length
+  local -i target_length=$content_length
+  for key in $HL_LAYOUT_ORDER; do
+    (( ! $HL_TRUNC_ORDER[(Ie)$key] )) && continue # no truncation specified
+    (( $trunc_initial_length >= $content_lengths[$key] )) && continue # already short enough
+    (( target_length -= $content_lengths[$key] - $trunc_initial_length ))
+  done
+
+  # Update first segment
+  for key in $HL_LAYOUT_ORDER; do
+    [[ $key == '_PRE' ]] && continue # skip special segment
+    (( content_lengths[$key] <= 0 && layout_lengths[$key] <= 0 )) && continue # skip omitted segment
+    if (( ${+HL_LAYOUT_FIRST[$key]} )); then
+      layouts[$key]=$HL_LAYOUT_FIRST[$key]
+      (( layout_length -= $layout_lengths[$key] - $first_layout_lengths[$key] ))
+      layout_lengths[$key]=$first_layout_lengths[$key]
+    fi
+    break
+  done
+
+  # Remove segments as needed
+  for key in $HL_TRUNC_ORDER; do
+    (( content_lengths[$key] <= 0 )) && continue # already removed
+    local -i remove=$(( $content_lengths[$key] < $trunc_initial_length ? $content_lengths[$key] : $trunc_initial_length ))
+    local -i offset=$(( $remove < $trunc_removal_length ? 0 : $remove - $trunc_removal_length ))
+    (( $target_length + $layout_length - $offset <= $COLUMNS )) && break # done removing
+    (( target_length -= $remove ))
+    contents[$key]=''; (( content_length -= $content_lengths[$key] )); content_lengths[$key]=0
+    layouts[$key]=''; (( layout_length -= $layout_lengths[$key] )); layout_lengths[$key]=0
+
+    # Update first segment
+    for key in $HL_LAYOUT_ORDER; do
+      [[ $key == '_PRE' ]] && continue # skip special segment
+      (( content_lengths[$key] <= 0 && layout_lengths[$key] <= 0 )) && continue # skip omitted segment
+      if (( ${+HL_LAYOUT_FIRST[$key]} )); then
+        layouts[$key]=$HL_LAYOUT_FIRST[$key]
+        (( layout_length -= $layout_lengths[$key] - $first_layout_lengths[$key] ))
+        layout_lengths[$key]=$first_layout_lengths[$key]
+      fi
+      break
+    done
+  done
+
+  # Truncate segments to initial length
+  for key in $HL_TRUNC_ORDER; do
+    (( content_lengths[$key] <= 0 )) && continue # segment removed
+    local -i excess=$(( $content_length + $layout_length - $COLUMNS ))
+    (( $excess <= 0 )) && break # done truncating
+    local -i removeable=$(( $content_lengths[$key] - $trunc_initial_length ))
+    (( $removeable <= 0 )) && continue # already short enough
+    local -i remove=$(( ( $excess < $removeable ? $excess : $removeable ) ))
+    (( content_length -= $remove ))
+    content_lengths[$key]=$(( $content_lengths[$key] - $remove ))
+    contents[$key]="$HL_TRUNC_SYMBOL${contents[$key]:$(( $remove + ${#HL_TRUNC_SYMBOL} ))}"
+  done
+
+  # Truncate segment to minimum length
+  for key in $HL_TRUNC_ORDER; do
+    (( content_lengths[$key] <= 0 )) && continue # segment removed
+    local -i excess=$(( $content_length + $layout_length - $COLUMNS ))
+    (( $excess <= 0 )) && break # done truncating
+    (( content_length -= $excess ))
+    content_lengths[$key]=$(( $content_lengths[$key] - $excess ))
+    contents[$key]="$HL_TRUNC_SYMBOL${contents[$key]:$(( excess + ${#HL_TRUNC_SYMBOL} ))}"
+  done
+
+  # Build spacer
+  local -i remainder=$(( $COLUMNS - $content_length - $layout_length ))
+  if (( $remainder > 0 )); then
+    contents[_SPACER]="$(headline-repeat-char "$HL_SPACE_CHAR" $(( $remainder + $layout_lengths[_SPACER] )) )"
+  fi
+
+  # Assemble segments
+  local information='' # the styled information line
+  for key in $HL_LAYOUT_ORDER; do
+    local segment=''; local segment_sep=''
+    if [[ ${key[1]} == '_' && ${#contents[$key]} == 0 ]]; then # special segment without content (ex: _PRE, _POST)
+      segment="${layouts[$key]}"; segment_sep=$segment
+    elif [[ ${key[1]} == '_' && ${#contents[$key]} != 0 ]]; then # special segment with generated content (ex: _SPACER)
+      segment="${contents[$key]}"; segment_sep=$segment
+    elif [[ ${key[1]} != '_' && ${#contents[$key]} != 0 ]]; then # normal segment with content
+      segment="${HL_CONTENT_TEMPLATE[$key]/$HL_TEMPLATE_TOKEN/$contents[$key]}"
+      segment="%{$reset%}$HL_BASE_STYLE$segment%{$reset%}$HL_BASE_STYLE$HL_LAYOUT_STYLE"
+      segment="${layouts[$key]/$HL_TEMPLATE_TOKEN/$segment}"
+    else # normal segment without content
+      continue
+    fi
+    information+="$HL_BASE_STYLE$HL_LAYOUT_STYLE$segment%{$reset%}"
+  done
+
+  # Assemble separator
+  local separator=$(headline-repeat-char "${HL_SEP[_LINE]}" $(( $COLUMNS - ${#HL_SEP[_PRE]} - ${#HL_SEP[_POST]} )) )
+  separator=$(headline-transfer-styles "$information" "${HL_SEP[_PRE]}$separator${HL_SEP[_POST]}")
+  separator="${separator//"%}"/"%}$HL_SEP_STYLE"}"
+
+  # Prepare cursor
+  local overwrite='false'
+  if [[ $HL_OVERWRITE == 'on' && $_HL_CMD_NUM == $_HL_CMD_NUM_PREV ]]; then
+    overwrite='true'
+    print -nr "$cursor_hide"
+    print -nr "$cursor_up" # to prompt line
+    (( ${#HL_OUTPUT_INFO} )) && print -nr "$cursor_up" # to info line
+    (( ${#HL_OUTPUT_SEP} )) && print -nr "$cursor_up" # to separator line
+    if [[ $HL_SEP_MODE == 'auto' && ! $HL_OUTPUT_SEP ]]; then
+      _HL_AT_TOP='true' # deduce that we were at top last time
+    fi
+    print -nr "$cursor_show"
+  fi
+
+  # Error line
+  if [[ $err != 0 && ($HL_ERR_MODE == 'on' || $HL_ERR_MODE == 'detail') && $overwrite != 'true' ]]; then
+    local message=$err
+    if [[ $HL_ERR_MODE == 'detail' ]]; then
+      local meaning=$(headline-exit-meaning $err)
+      (( ${#meaning} > 0 )) && message+="${HL_ERR_DETAIL_TEMPLATE/$HL_TEMPLATE_TOKEN/$meaning}%{$reset%}"
+    fi
+    print -rP "${HL_ERR_TEMPLATE/$HL_TEMPLATE_TOKEN/$message}%{$reset%}"
+  fi
+
+  # Separator line
+  if [[ $HL_SEP_MODE == 'on' || ($HL_SEP_MODE == 'auto' && $_HL_AT_TOP != 'true') ]]; then
+    HL_OUTPUT_SEP=$separator
+    [[ $HL_PRINT_MODE == 'precmd' ]] && print -rP "$HL_OUTPUT_SEP"
+  else
+    HL_OUTPUT_SEP=''
+  fi
+  _HL_SEP=$separator
+
+  # Information line
+  if [[ $HL_INFO_MODE == 'on' || ($HL_INFO_MODE == 'auto' && $information != $_HL_INFO) || $overwrite == 'true' ]]; then
+    HL_OUTPUT_INFO=$information
+    [[ $HL_PRINT_MODE == 'precmd' ]] && print -rP "$HL_OUTPUT_INFO"
+  else
+    HL_OUTPUT_INFO=''
+  fi
+  _HL_INFO=$information
+
+  # Prompt
+  if [[ $HL_PRINT_MODE == 'prompt' ]]; then
+    PROMPT='$('
+    (( ${#HL_OUTPUT_SEP} )) && PROMPT+='print -rP "$HL_OUTPUT_SEP"; '
+    (( ${#HL_OUTPUT_INFO} )) && PROMPT+='print -rP "$HL_OUTPUT_INFO"; '
+    PROMPT+='print -rP "$HL_PROMPT")'
+  else
+    PROMPT=$HL_PROMPT
+  fi
+
+  # Right prompt
+  if [[ $HL_CLOCK_MODE == 'on' ]]; then
+    RPROMPT='${HL_CLOCK_TEMPLATE/$HL_TEMPLATE_TOKEN/$(eval ${=HL_CLOCK_SOURCE})}%{$reset%}$HL_RPROMPT'
+  else
+    RPROMPT=$HL_RPROMPT
+  fi
+
+  _HL_CMD_NUM_PREV=$_HL_CMD_NUM
+  _HL_AT_TOP='false'
+}
-- 
GitLab