Skip to content

Commit

Permalink
Make Print Functions Consistent with --keep-empty-lines
Browse files Browse the repository at this point in the history
* Updating `batslib_count_lines` to automatically detect trailing newlines and count them if present.
  * This works on the concept that a string has a trailing newline if it was intended -- e.g. via `run --keep-empty-lines` or similar.
* Updating `batslib_prefix` and `batslib_mark` to accept `--keep-empty-lines` option to properly print empty lines and trailing newlines.
* Updating `batslib_print_kv_single_or_multi` calls to `batslib_prefix`.
  * Will automatically detect trailing new lines (on a per-value basis).
  * If trailing newline is present, `--keep-empty-lines` will be passed to `batslib_prefix`.

This implements option 4 for `batslib_count_lines` and `batslib_print_kv_single_or_multi`, and option 2 for `batslib_prefix` and `batslib_mark`, as discussed in bats-core#11.

Fixes bats-core#11.
  • Loading branch information
CauhxMilloy committed May 8, 2024
1 parent 9bf10e8 commit dc50d97
Showing 1 changed file with 60 additions and 15 deletions.
75 changes: 60 additions & 15 deletions src/output.bash
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,8 @@ batslib_err() {
}

# Count the number of lines in the given string.
#
# TODO(ztombol): Fix tests and remove this note after #93 is resolved!
# NOTE: Due to a bug in Bats, `batslib_count_lines "$output"' does not
# give the same result as `${#lines[@]}' when the output contains
# empty lines.
# See PR #93 (https://github.com/sstephenson/bats/pull/93).
# If the string has a trailing newline, it is interpretted as being generated with `run --keep-empty-lines` or similar.
# This function will then read and count all empty lines and trailing newlines.
#
# Globals:
# none
Expand All @@ -60,11 +56,22 @@ batslib_err() {
# Outputs:
# STDOUT - number of lines
batslib_count_lines() {
local -r input="$1"

local line=''
local -i n_lines=0
local line
while IFS='' read -r line || [[ -n $line ]]; do
# Read and count each line, until the last line (handled below).
while IFS='' read -r line; do
(( ++n_lines ))
done < <(printf '%s' "$input")
# If there's a detectable trailing newline, it's there on purpose (e.g. `run --keep-empty-lines`),
# then empty lines must be properly handled.
# Or, if the last line was not empty, it needs to be counted.
if [[ "${input: -1}" = $'\n' ]] || [[ -n "$line" ]]; then
# Increment for the EOF (not captured by the loop above).
(( ++n_lines ))
done < <(printf '%s' "$1")
fi

echo "$n_lines"
}

Expand Down Expand Up @@ -167,7 +174,8 @@ batslib_print_kv_multi() {
#
# Otherwise, print all pairs in multi-line format after indenting values
# with two spaces for readability (identical to using `batslib_prefix'
# and `batslib_print_kv_multi')
# and `batslib_print_kv_multi'). For each value, if it contains a trailing
# newline, it will be printed keeping all empty lines / trailing newlines.
#
# Globals:
# none
Expand All @@ -194,7 +202,12 @@ batslib_print_kv_single_or_multi() {
else
local -i i
for (( i=1; i < ${#pairs[@]}; i+=2 )); do
pairs[$i]="$( batslib_prefix < <(printf '%s' "${pairs[$i]}") )"
# If value has trailing newline, keep it.
if [[ "${pairs[$i]: -1}" = $'\n' ]]; then
pairs[$i]="$( batslib_prefix --keep-empty-lines < <(printf '%s' "${pairs[$i]}") )"
else
pairs[$i]="$( batslib_prefix < <(printf '%s' "${pairs[$i]}") )"
fi
done
batslib_print_kv_multi "${pairs[@]}"
fi
Expand All @@ -205,6 +218,7 @@ batslib_print_kv_single_or_multi() {
# Globals:
# none
# Arguments:
# --keep-empty-lines - optional. preserves empty lines and trailing newlines.
# $1 - [= ] prefix string
# Returns:
# none
Expand All @@ -213,11 +227,25 @@ batslib_print_kv_single_or_multi() {
# Outputs:
# STDOUT - prefixed lines
batslib_prefix() {
local -i keep_empty_lines=0
if [ "${1-}" = '--keep-empty-lines' ]; then
keep_empty_lines=1
shift
fi
local -r prefix="${1:- }"
local line
while IFS='' read -r line || [[ -n $line ]]; do

local line=''
local -i lines_read=0
# Read and print each line, until the last line (handled below).
while IFS='' read -r line; do
printf '%s%s\n' "$prefix" "$line"
(( ++lines_read ))
done
# Print for the last line with EOF (not captured by the loop above).
# The last read line must be non-empty, or `keep_empty_lines` with non-empty stdin.
if (( keep_empty_lines )) && (( lines_read > 0 )) || [[ -n "$line" ]]; then
printf '%s%s\n' "$prefix" "$line"
fi
}

# Mark select lines of the text read from the standard input by
Expand All @@ -229,6 +257,7 @@ batslib_prefix() {
# Globals:
# none
# Arguments:
# --keep-empty-lines - optional. preserves empty lines and trailing newlines.
# $1 - marking string
# $@ - indices (zero-based) of lines to mark
# Returns:
Expand All @@ -238,13 +267,19 @@ batslib_prefix() {
# Outputs:
# STDOUT - lines after marking
batslib_mark() {
local -i keep_empty_lines=0
if [ "${1-}" = '--keep-empty-lines' ]; then
keep_empty_lines=1
shift
fi
local -r symbol="$1"; shift
# Sort line numbers.
set -- $( sort -nu <<< "$( printf '%d\n' "$@" )" )

local line
local line=''
local -i idx=0
while IFS='' read -r line || [[ -n $line ]]; do
# Read and print each line, until the last line (handled below).
while IFS='' read -r line; do
if (( ${1:--1} == idx )); then
printf '%s\n' "${symbol}${line:${#symbol}}"
shift
Expand All @@ -253,6 +288,16 @@ batslib_mark() {
fi
(( ++idx ))
done
# Print for the last line with EOF (not captured by the loop above).
# The last read line must be non-empty, or `keep_empty_lines` with non-empty stdin.
if (( keep_empty_lines )) && (( idx > 0 )) || [[ -n "$line" ]]; then
if (( ${1:--1} == idx )); then
printf '%s\n' "${symbol}${line:${#symbol}}"
shift
else
printf '%s\n' "$line"
fi
fi
}

# Enclose the input text in header and footer lines.
Expand Down

0 comments on commit dc50d97

Please sign in to comment.