diff options
-rw-r--r-- | src/nvim/channel.c | 48 | ||||
-rw-r--r-- | test/functional/fixtures/shell-test.c | 4 | ||||
-rw-r--r-- | test/functional/terminal/buffer_spec.lua | 15 |
3 files changed, 52 insertions, 15 deletions
diff --git a/src/nvim/channel.c b/src/nvim/channel.c index ebeaffe5a1..0222a134a7 100644 --- a/src/nvim/channel.c +++ b/src/nvim/channel.c @@ -29,6 +29,7 @@ #include "nvim/log.h" #include "nvim/lua/executor.h" #include "nvim/main.h" +#include "nvim/mbyte.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/msgpack_rpc/channel.h" @@ -646,35 +647,52 @@ static inline list_T *buffer_to_tv_list(const char *const buf, const size_t len) void on_channel_data(Stream *stream, RBuffer *buf, size_t count, void *data, bool eof) { Channel *chan = data; - on_channel_output(stream, chan, buf, count, eof, &chan->on_data); + on_channel_output(stream, chan, buf, eof, &chan->on_data); } void on_job_stderr(Stream *stream, RBuffer *buf, size_t count, void *data, bool eof) { Channel *chan = data; - on_channel_output(stream, chan, buf, count, eof, &chan->on_stderr); + on_channel_output(stream, chan, buf, eof, &chan->on_stderr); } -static void on_channel_output(Stream *stream, Channel *chan, RBuffer *buf, size_t count, bool eof, +static void on_channel_output(Stream *stream, Channel *chan, RBuffer *buf, bool eof, CallbackReader *reader) { - // stub variable, to keep reading consistent with the order of events, only - // consider the count parameter. - size_t r; - char *ptr = rbuffer_read_ptr(buf, &r); + size_t count; + char *output = rbuffer_read_ptr(buf, &count); - if (eof) { - reader->eof = true; - } else { - if (chan->term) { - terminal_receive(chan->term, ptr, count); + if (chan->term) { + if (!eof) { + char *p = output; + char *end = output + count; + while (p < end) { + // Don't pass incomplete UTF-8 sequences to libvterm. #16245 + // Composing chars can be passed separately, so utf_ptr2len_len() is enough. + int clen = utf_ptr2len_len(p, (int)(end - p)); + if (clen > end - p) { + count = (size_t)(p - output); + break; + } + p += clen; + } } + terminal_receive(chan->term, output, count); + } + + if (count) { rbuffer_consumed(buf, count); + } + // Move remaining data to start of buffer, so the buffer can never wrap around. + rbuffer_reset(buf); - if (callback_reader_set(*reader)) { - ga_concat_len(&reader->buffer, ptr, count); - } + if (callback_reader_set(*reader)) { + ga_concat_len(&reader->buffer, output, count); + } + + if (eof) { + reader->eof = true; } if (callback_reader_set(*reader)) { diff --git a/test/functional/fixtures/shell-test.c b/test/functional/fixtures/shell-test.c index ef9f23e3f9..bd71e7d11b 100644 --- a/test/functional/fixtures/shell-test.c +++ b/test/functional/fixtures/shell-test.c @@ -49,6 +49,10 @@ int main(int argc, char **argv) help(); } +#ifdef _MSC_VER + SetConsoleOutputCP(CP_UTF8); +#endif + if (argc >= 2) { if (strcmp(argv[1], "-t") == 0) { if (argc < 3) { diff --git a/test/functional/terminal/buffer_spec.lua b/test/functional/terminal/buffer_spec.lua index 0c6f74e583..741aaf2fe0 100644 --- a/test/functional/terminal/buffer_spec.lua +++ b/test/functional/terminal/buffer_spec.lua @@ -10,6 +10,7 @@ local pcall_err = helpers.pcall_err local eq, neq = helpers.eq, helpers.neq local api = helpers.api local retry = helpers.retry +local testprg = helpers.testprg local write_file = helpers.write_file local command = helpers.command local exc_exec = helpers.exc_exec @@ -395,6 +396,20 @@ describe(':terminal buffer', function() eq('a' .. composing:rep(5), api.nvim_get_current_line()) end) end) + + it('handles split UTF-8 sequences #16245', function() + local screen = Screen.new(50, 7) + screen:attach() + fn.termopen({ testprg('shell-test'), 'UTF-8' }) + screen:expect([[ + ^å | + ref: å̲ | + 1: å̲ | + 2: å̲ | + 3: å̲ | + |*2 + ]]) + end) end) describe('on_lines does not emit out-of-bounds line indexes when', function() |