diff --git a/scripts/helpers.sh b/scripts/helpers.sh index f441eb2..606892c 100644 --- a/scripts/helpers.sh +++ b/scripts/helpers.sh @@ -209,22 +209,13 @@ scrolling_text() { # Get padding from tmux option local padding padding="$(get_nowplaying_option "@nowplaying_scroll_padding")" + # Duplicating the text handles slices that cross the scroll cycle boundary. local padded_text="${text}${padding}${text}" - local padded_length="${#padded_text}" - + # Calculate the starting position based on offset local start_pos=$((offset % (text_length + ${#padding}))) - - # Extract the visible portion efficiently - # If we can get the whole substring without wrapping - if [ $((start_pos + max_width)) -le "$padded_length" ]; then - printf "%.*s\n" "$max_width" "${padded_text:$start_pos}" - else - # Need to wrap around - get first part and second part - local first_part_len=$((padded_length - start_pos)) - local second_part_len=$((max_width - first_part_len)) - printf "%s%.*s\n" "${padded_text:$start_pos}" "$second_part_len" "$padded_text" - fi + + printf '%.*s\n' "$max_width" "${padded_text:$start_pos}" } # Get current time in seconds for scrolling offset diff --git a/scripts/test.sh b/scripts/test.sh index faae1bb..2b5197d 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -67,6 +67,24 @@ assert_adapter_rejected() { assert_eq "" "$track_title" "${message} clears title" } +assert_scrolling_text() { + local expected="$1" + local text="$2" + local width="$3" + local padding="$4" + local offset="$5" + local message="$6" + local actual + + actual="$(bash -c ' + source "$1" + scroll_padding="$5" + get_nowplaying_option() { printf "%s" "$scroll_padding"; } + scrolling_text "$2" "$3" "$4" + ' _ "${ROOT_DIR}/scripts/helpers.sh" "$text" "$width" "$offset" "$padding")" + assert_eq "$expected" "$actual" "$message" +} + write_tmux_mock() { cat > "${TMP_DIR}/tmux" <<'MOCK' #!/usr/bin/env bash @@ -318,6 +336,15 @@ assert_adapter_rejected $'\tArtist\tTitle' "empty-status adapter output" assert_adapter_rejected $'Playing\tArtist\tTitle\nSecond' "multiline adapter output" assert_adapter_rejected $'Playing\tArtist\tTitle\r' "carriage-return adapter output" +assert_scrolling_text "abcde" "abcde" 5 "::" 99 "scrolling returns text that exactly fits" +assert_scrolling_text "abcd" "abcde" 4 "::" 0 "scrolling starts at first position" +assert_scrolling_text "e::a" "abcde" 4 "::" 4 "scrolling crosses into padding" +assert_scrolling_text "::ab" "abcde" 4 "::" 5 "scrolling starts at padding" +assert_scrolling_text ":abc" "abcde" 4 "::" 6 "scrolling starts at last cycle offset" +assert_scrolling_text "abcd" "abcde" 4 "::" 7 "scrolling wraps at cycle length" +assert_scrolling_text ":abc" "abcde" 4 "::" 13 "scrolling normalizes large offsets" +assert_scrolling_text "eabc" "abcde" 4 "" 4 "scrolling crosses empty-padding boundary" + write_uname_mock write_playerctl_mock playing