From 582c3c56b218b9be9745efc69c2eb282bbe99b67 Mon Sep 17 00:00:00 2001 From: Johannes Altmanninger Date: Sun, 28 Jan 2024 18:23:08 +0100 Subject: [PATCH] Do not add trailing newline to non-scrolling fifo buffers Internally, all lines have a trailing "\n". Buffers created empty (like fifo buffers) start with a single line. When reading data into fifo buffers, we insert *before* the last line's trailing newline ("last newline"). This enables autoscrolling (enabled with "edit -scroll") as long as the cursor is on the last newline. When autoscrolling is disabled, we have a special case to insert *after* the last newline. This means that a cursor on that newline won't be moved. Then we transplant the newline character from the beginning to the end of the buffer. This special case happens only on the very first fifo read; on subsequent reads, the cursor at position 1.1 will not be moved anway because insertions happen below 1.1. Since we always insert (effectively) before the last newline, fifo buffers have a trailing empty line. For autoscrolling buffers this seems correct; it gives users an obvious way to toggle autoscrolling. For non-scrolling buffers the newline is redundant. Remove it. This requires keeping track of whether the last newline comes from the fifo, or was added by us. The shortest fix I could find is to always append to the buffer if not scrolling, and then delete the added newline character if applicable. m_buffer.insert(m_scroll ? pos : m_buffer.next(pos), StringView(data, data+count)); if (not m_scroll and not m_had_trailing_newline) m_buffer.erase(pos, m_buffer.next(pos)); maybe that's the best fix overall; but erasing at the end seems better than erasing in the middle, so do that whenever possible. Reported in https://lists.sr.ht/~mawww/kakoune/%3CZbTK7qit9nzvrMkx@gmail.com%3E --- src/buffer_utils.cc | 22 ++++++++++++---------- test/commands/edit-fifo-noscroll/cmd | 1 + test/commands/edit-fifo-noscroll/out | 3 +++ test/commands/edit-fifo-noscroll/script | 19 +++++++++++++++++++ 4 files changed, 35 insertions(+), 10 deletions(-) create mode 100644 test/commands/edit-fifo-noscroll/cmd create mode 100644 test/commands/edit-fifo-noscroll/out create mode 100644 test/commands/edit-fifo-noscroll/script diff --git a/src/buffer_utils.cc b/src/buffer_utils.cc index 6de07c71..21ec7092 100644 --- a/src/buffer_utils.cc +++ b/src/buffer_utils.cc @@ -205,7 +205,7 @@ Buffer* create_fifo_buffer(String name, int fd, Buffer::Flags flags, bool scroll m_buffer.flags() &= ~(Buffer::Flags::Fifo | Buffer::Flags::NoUndo); } - void read_fifo() const + void read_fifo() { kak_assert(m_buffer.flags() & Buffer::Flags::Fifo); @@ -234,20 +234,21 @@ Buffer* create_fifo_buffer(String name, int fd, Buffer::Flags flags, bool scroll } auto pos = m_buffer.back_coord(); - const bool prevent_scrolling = pos == BufferCoord{0,0} and not m_scroll; - if (prevent_scrolling) + const bool is_first = pos == BufferCoord{0,0}; + if (not m_scroll and (is_first or m_had_trailing_newline)) pos = m_buffer.next(pos); - m_buffer.insert(pos, StringView(data, data+count)); + pos = m_buffer.insert(pos, StringView(data, data+count)).end; - if (prevent_scrolling) + bool have_trailing_newline = (data[count-1] == '\n'); + if (not m_scroll) { - m_buffer.erase({0,0}, m_buffer.next({0,0})); - // in the other case, the buffer will have automatically - // inserted a \n to guarantee its invariant. - if (data[count-1] == '\n') - m_buffer.insert(m_buffer.end_coord(), "\n"); + if (is_first) + m_buffer.erase({0,0}, m_buffer.next({0,0})); + else if (not m_had_trailing_newline and have_trailing_newline) + m_buffer.erase(pos, m_buffer.next(pos)); } + m_had_trailing_newline = have_trailing_newline; } while (++loop < max_loop and fd_readable(fifo)); } @@ -263,6 +264,7 @@ Buffer* create_fifo_buffer(String name, int fd, Buffer::Flags flags, bool scroll Buffer& m_buffer; bool m_scroll; + bool m_had_trailing_newline; }; buffer->values()[fifo_watcher_id] = Value(Meta::Type{}, fd, *buffer, scroll); diff --git a/test/commands/edit-fifo-noscroll/cmd b/test/commands/edit-fifo-noscroll/cmd new file mode 100644 index 00000000..9a2627d5 --- /dev/null +++ b/test/commands/edit-fifo-noscroll/cmd @@ -0,0 +1 @@ +:nop %sh{mkfifo fifo}; edit -fifo fifo *fifo* diff --git a/test/commands/edit-fifo-noscroll/out b/test/commands/edit-fifo-noscroll/out new file mode 100644 index 00000000..e0ee5094 --- /dev/null +++ b/test/commands/edit-fifo-noscroll/out @@ -0,0 +1,3 @@ +* line1 +* line2 +2049 diff --git a/test/commands/edit-fifo-noscroll/script b/test/commands/edit-fifo-noscroll/script new file mode 100644 index 00000000..10fe9340 --- /dev/null +++ b/test/commands/edit-fifo-noscroll/script @@ -0,0 +1,19 @@ +ui_out -ignore 7 +exec 5>fifo +ui_out '{ "jsonrpc": "2.0", "method": "refresh", "params": [true] }' + +echo '* line1' >&5 +ui_out '{ "jsonrpc": "2.0", "method": "draw", "params": [[[{ "face": { "fg": "black", "bg": "white", "underline": "default", "attributes": [] }, "contents": "*" }, { "face": { "fg": "default", "bg": "default", "underline": "default", "attributes": [] }, "contents": " line1\u000a" }]], { "fg": "default", "bg": "default", "underline": "default", "attributes": [] }, { "fg": "blue", "bg": "default", "underline": "default", "attributes": [] }] }' +ui_out -ignore 2 + +echo '* line2' >&5 +ui_out '{ "jsonrpc": "2.0", "method": "draw", "params": [[[{ "face": { "fg": "black", "bg": "white", "underline": "default", "attributes": [] }, "contents": "*" }, { "face": { "fg": "default", "bg": "default", "underline": "default", "attributes": [] }, "contents": " line1\u000a" }], [{ "face": { "fg": "default", "bg": "default", "underline": "default", "attributes": [] }, "contents": "* line2\u000a" }]], { "fg": "default", "bg": "default", "underline": "default", "attributes": [] }, { "fg": "blue", "bg": "default", "underline": "default", "attributes": [] }] }' +ui_out -ignore 2 + +dd if=/dev/zero bs=2049 count=1 2>/dev/null | sed s/././g >&5 +ui_out -ignore 3 +ui_in '{ "jsonrpc": "2.0", "method": "keys", "params": [ "gjxH|wc -c" ] }' +ui_out -ignore 6 + +exec 5>&- +ui_out '{ "jsonrpc": "2.0", "method": "draw_status", "params": [[], [{ "face": { "fg": "default", "bg": "default", "underline": "default", "attributes": [] }, "contents": "*fifo* 3:4 " }, { "face": { "fg": "black", "bg": "yellow", "underline": "default", "attributes": [] }, "contents": "[scratch]" }, { "face": { "fg": "default", "bg": "default", "underline": "default", "attributes": [] }, "contents": " " }, { "face": { "fg": "blue", "bg": "default", "underline": "default", "attributes": [] }, "contents": "1 sel" }, { "face": { "fg": "default", "bg": "default", "underline": "default", "attributes": [] }, "contents": " - client0@[kak-tests]" }], { "fg": "cyan", "bg": "default", "underline": "default", "attributes": [] }] }'