From 07fe27ff730067a1783fda8d3a5ce11d7fe092a3 Mon Sep 17 00:00:00 2001 From: Ira Abramov Date: Sat, 18 Apr 2026 18:42:30 +0300 Subject: [PATCH 1/3] fix(themes/powerline): ensure prompt starts on a new line When a command's output has no trailing newline, \e[G (column 1) in PS1 would overwrite that output with the prompt. Add a missing-newline indicator in PROMPT_COMMAND: reversed-video % marks the incomplete line, then \r\e[K moves to column 1 and clears it, so the prompt always renders cleanly regardless of the previous command's output. Fixes #2372 Co-Authored-By: Claude Sonnet 4.6 --- themes/powerline/powerline.base.bash | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/themes/powerline/powerline.base.bash b/themes/powerline/powerline.base.bash index 85ba0dce93..75e8886eac 100644 --- a/themes/powerline/powerline.base.bash +++ b/themes/powerline/powerline.base.bash @@ -290,6 +290,10 @@ function __powerline_prompt_command() { local beginning_of_line='\[\e[G\]' local info prompt_color segment prompt + # Reversed % marks where a missing trailing newline was; \r\e[K then + # clears the line so the prompt always starts cleanly at column 1. + printf '%b' '\e[7m%\e[0m\r\e[K' + local LEFT_PROMPT="" local SEGMENTS_AT_LEFT=0 local LAST_SEGMENT_COLOR="" From 36e586a526bacae65a7fb227c4ab91099a514b12 Mon Sep 17 00:00:00 2001 From: Ira Abramov Date: Sat, 18 Apr 2026 18:53:05 +0300 Subject: [PATCH 2/3] test(themes/powerline): add BATS tests for missing-newline handler Verifies that __powerline_prompt_command: - outputs the \e[7m%\e[0m\r\e[K sequence to stdout - outputs it as the very first bytes (before segment/PS1 content) Uses a no-op stub segment and a _save-and-reload-history stub to avoid pulling in every plugin that the default segments depend on. Co-Authored-By: Claude Sonnet 4.6 --- test/themes/powerline.base.bats | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 test/themes/powerline.base.bats diff --git a/test/themes/powerline.base.bats b/test/themes/powerline.base.bats new file mode 100644 index 0000000000..210ad39461 --- /dev/null +++ b/test/themes/powerline.base.bats @@ -0,0 +1,32 @@ +# shellcheck shell=bats +# shellcheck disable=SC2034 # Variables consumed by externally-loaded powerline functions. + +load "${MAIN_BASH_IT_DIR?}/test/test_helper.bash" + +function local_setup_file() { + setup_libs "colors" + load "${BASH_IT?}/themes/powerline/powerline.base.bash" +} + +# Stub a no-op segment so we can run __powerline_prompt_command without +# sourcing every plugin that the default segments depend on. +function __powerline_noop_prompt() { :; } +# _save-and-reload-history is called unconditionally; stub it out. +function _save-and-reload-history() { :; } + +# --- __powerline_prompt_command: missing-newline handler (fixes #2372) --- + +@test "powerline base: __powerline_prompt_command outputs missing-newline escape sequence to stdout" { + POWERLINE_PROMPT=("noop") + run __powerline_prompt_command + assert_output --partial $'\e[7m%\e[0m\r\e[K' +} + +@test "powerline base: __powerline_prompt_command missing-newline sequence is first output" { + POWERLINE_PROMPT=("noop") + run __powerline_prompt_command + # Use bash pattern match to confirm the sequence appears at the very start, + # meaning it is a direct printf rather than anything embedded in PS1 or segments. + local prefix=$'\e[7m%\e[0m\r\e[K' + [[ "${output}" == "${prefix}"* ]] +} From eec96aa97b7444ea29c9bb99f04c68915e3f0654 Mon Sep 17 00:00:00 2001 From: Ira Abramov Date: Tue, 21 Jul 2026 18:16:53 +0300 Subject: [PATCH 3/3] fix(themes/powerline): overflow line with spaces to force newline wrap Replace the reversed-% + \e[K approach (which cleared the previous command's unterminated output instead of preserving it) with the fix suggested by @joshiste in #2372: pad the current line with COLUMNS-1 spaces so the terminal auto-wraps the cursor to a fresh line, then carriage-return to column 1. This preserves any partial output from the previous command instead of erasing it. The \e[G reset in PS1 is now redundant since the cursor is already at column 1 by the time PS1 is rendered, so beginning_of_line is removed. Fixes #2372 --- test/themes/powerline.base.bats | 27 ++++++++++++++++++++++----- themes/powerline/powerline.base.bash | 10 +++++----- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/test/themes/powerline.base.bats b/test/themes/powerline.base.bats index 210ad39461..f98c3e7040 100644 --- a/test/themes/powerline.base.bats +++ b/test/themes/powerline.base.bats @@ -16,17 +16,34 @@ function _save-and-reload-history() { :; } # --- __powerline_prompt_command: missing-newline handler (fixes #2372) --- -@test "powerline base: __powerline_prompt_command outputs missing-newline escape sequence to stdout" { +@test "powerline base: __powerline_prompt_command overflows the line and returns to column 1" { + COLUMNS=20 POWERLINE_PROMPT=("noop") run __powerline_prompt_command - assert_output --partial $'\e[7m%\e[0m\r\e[K' + + local expected + expected="$(printf '%*s\r' "$((COLUMNS - 1))" '')" + assert_output --partial "${expected}" } @test "powerline base: __powerline_prompt_command missing-newline sequence is first output" { + COLUMNS=20 POWERLINE_PROMPT=("noop") run __powerline_prompt_command - # Use bash pattern match to confirm the sequence appears at the very start, - # meaning it is a direct printf rather than anything embedded in PS1 or segments. - local prefix=$'\e[7m%\e[0m\r\e[K' + + # Confirm the padding+carriage-return appears at the very start, meaning it + # is a direct printf rather than anything embedded in PS1 or segments. + local prefix + prefix="$(printf '%*s\r' "$((COLUMNS - 1))" '')" [[ "${output}" == "${prefix}"* ]] } + +@test "powerline base: __powerline_prompt_command falls back to 80 columns when COLUMNS is unset" { + unset COLUMNS + POWERLINE_PROMPT=("noop") + run __powerline_prompt_command + + local expected + expected="$(printf '%*s\r' 79 '')" + assert_output --partial "${expected}" +} diff --git a/themes/powerline/powerline.base.bash b/themes/powerline/powerline.base.bash index 75e8886eac..7d7af0141e 100644 --- a/themes/powerline/powerline.base.bash +++ b/themes/powerline/powerline.base.bash @@ -287,12 +287,12 @@ function __powerline_last_status_prompt() { function __powerline_prompt_command() { local last_status="$?" ## always the first - local beginning_of_line='\[\e[G\]' local info prompt_color segment prompt - # Reversed % marks where a missing trailing newline was; \r\e[K then - # clears the line so the prompt always starts cleanly at column 1. - printf '%b' '\e[7m%\e[0m\r\e[K' + # If the previous command left output without a trailing newline, overflow + # the rest of the line with spaces so the terminal auto-wraps the cursor + # onto a fresh line before the prompt is drawn (mirrors zsh's PROMPT_SP). + printf '%*s\r' "$((${COLUMNS:-80} - 1))" '' local LEFT_PROMPT="" local SEGMENTS_AT_LEFT=0 @@ -334,5 +334,5 @@ function __powerline_prompt_command() { prompt+=" " fi - PS1="${beginning_of_line}${normal?}${LEFT_PROMPT}${prompt}" + PS1="${normal?}${LEFT_PROMPT}${prompt}" }