Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Re: [PATCH] use clear-to-end to reduce trailing whitespace



Bart Schaefer <schaefer@xxxxxxxxxxxxxxxx> writes:

> On Sun, Jan 19, 2025 at 3:52 PM Daniel Colascione <dancol@xxxxxxxxxx> wrote:
>>
>> The attached new version of the patch should handle right prompts too. I
>> tested it with both zero and non-zero right prompt margins.
>
> Thanks, a couple of remaining notes:
>
> Your patch has Windows CRLF line endings, so won't apply cleanly to
> the base source, which has only UNIX/Linux LF line endings.
>
> Please don't use ".diff", ".patch", etc. extensions on attachments.  I
> know it's a little annoying given proclivities of email clients and
> IDEs, but we (and the archive software) prefer plain text attachments
> -- please either force Content-Type: text/plain or use a ".txt"
> extension.

Thanks. I'll keep all this in mind if we need another revision of the patch.

>> I also tested a bunch of terminals. I was unable to get any of them to
>> mistakenly scroll due to issuing a clear-to-end-of-line at the right
>> edge of the screen.
>
> Thanks.  Re-reading more closely the comment "2:" that you edited,
> another test case would be:
>
> * The cursor is somewhere above the bottom of the screen, and
> * the current line is exactly filled with text (even if possibly whitespace), so
> * therefore the terminal has already auto-margin'd to the next line, and
> * consequently emitting CEOL would erase the line below the expected
> line, which may clobber residual text from previous output

I haven't been able to get CEOL after wrap to erase the next line in the
terminals I mentioned previously.  See the attached Python program.

import curses
import sys
import time

def test_corner_clear():
    curses.setupterm()
    stdout = sys.stdout.buffer

    clear_to_eol = curses.tigetstr('el')
    cursor_address = curses.tigetstr('cup')
    cols = curses.tigetnum('cols')
    rows = curses.tigetnum('lines')
    has_am = curses.tigetflag('am')

    def _flush_and_wait():
        stdout.flush()
        time.sleep(1)

    print(f"Terminal has automatic margins: {has_am}")
    print(f"Clear to EOL sequence length: {len(clear_to_eol)} bytes")
    print(f"Clear to EOL sequence (raw): {repr(clear_to_eol)}")
    print(f"Terminal size: {rows} rows x {cols} columns (coordinates are 0-based: rows 0-{rows-1}, cols 0-{cols-1})")
    print("\nStarting EOL clear test...")

    # Fill first line with XY pattern
    stdout.write(curses.tparm(cursor_address, rows-2, 0))
    test_line1 = (b'XY'*((cols + 1) // 2))[:cols]
    stdout.write(test_line1)
    _flush_and_wait()

    # Fill second line with 12 pattern
    stdout.write(curses.tparm(cursor_address, rows-1, 0))
    test_line2 = (b'12'*((cols + 1) // 2))[:cols]
    stdout.write(test_line2)
    _flush_and_wait()

    # Move back to end of XY line and write !
    stdout.write(curses.tparm(cursor_address, rows-2, cols-1))
    _flush_and_wait()
    stdout.write(b'!')  # This may wrap to next line due to auto-margins
    _flush_and_wait()

    stdout.write(clear_to_eol)  # Clear to end of line - which line gets cleared?
    _flush_and_wait()

    stdout.write(b'>')  # Mark where we ended up
    _flush_and_wait()

    # Move to safe location for results
    stdout.write(curses.tparm(cursor_address, rows+1, 0))
    print("\nTest sequence (on rows", rows-2, "and", rows-1, "):")
    print("1. Wrote XY pattern on first line")
    print("2. Wrote 12 pattern on second line")
    print("3. Moved to end of XY line")
    print("4. Wrote '!' (which may trigger auto-margins)")
    print("5. Sent clear-to-EOL sequence")
    print("6. Wrote '>' marker")
    print("\nCheck:")
    print("1. Did '!' appear at end of XY line or start of 12 line?")
    print("2. Which line got cleared - end of XY line or part of 12 line?")
    print("3. Where did the '>' marker appear?")

def main():
    try:
        test_corner_clear()
    except KeyboardInterrupt:
        print("\nTest interrupted.")
    except Exception as e:
        print(f"\nError during test: {e}")
    finally:
        sys.stdout.buffer.write(curses.tparm(curses.tigetstr('cup'), curses.tigetnum('lines'), 0))
        sys.stdout.buffer.flush()

if __name__ == "__main__":
    main()

Why would we ever see this case though?  If we're exactly filling up the
window, there's no point to emitting CEOL.  Does the code do it anyway?

> There was a discussion about use of CEOS back in
> https://zsh.org/users/29197 for which there are already workarounds
> (mentioned in subsequent thread).  There may be a similar issue here.

The thread above is about clear-to-end-of-screen here, not
clear-to-end-of-line as we're discussing here.  Other shells use CEOL
without major trouble, yes?


Messages sorted by: Reverse Date, Date, Thread, Author