diff options
author | Enan Ajmain <3nan.ajmain@gmail.com> | 2022-12-09 02:55:50 +0600 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-12-08 12:55:50 -0800 |
commit | 1e2cc688891c789f02699f9c7e0bffd435794310 (patch) | |
tree | a966336897d3a1668e51d299ed3b0413da861712 | |
parent | 9b2c79034416c9a1b105f796b761a0d4a2a06ea1 (diff) | |
download | rneovim-1e2cc688891c789f02699f9c7e0bffd435794310.tar.gz rneovim-1e2cc688891c789f02699f9c7e0bffd435794310.tar.bz2 rneovim-1e2cc688891c789f02699f9c7e0bffd435794310.zip |
fix(chansend): sending lines to terminal in reverse order on Windows #19315
Problem: `chansend()` on Windows sends lines in reverse order.
Cause: Using \n instead of \r\n for newlines on Windows.
Solution: on Windows, use CRLF newline characters.
Fixes #18501
-rw-r--r-- | src/nvim/eval.c | 12 | ||||
-rw-r--r-- | src/nvim/eval/funcs.c | 11 | ||||
-rw-r--r-- | test/functional/terminal/channel_spec.lua | 15 |
3 files changed, 32 insertions, 6 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 554771d9c9..8734700305 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -5426,7 +5426,7 @@ void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, bool retlist // get input to the shell command (if any), and its length ptrdiff_t input_len; - char *input = save_tv_as_string(&argvars[1], &input_len, false); + char *input = save_tv_as_string(&argvars[1], &input_len, false, false); if (input_len < 0) { assert(input == NULL); return; @@ -5921,9 +5921,10 @@ bool read_blob(FILE *const fd, blob_T *const blob) /// @param[in] tv Value to store as a string. /// @param[out] len Length of the resulting string or -1 on error. /// @param[in] endnl If true, the output will end in a newline (if a list). +/// @param[in] crlf If true, list items will be joined with CRLF (if a list). /// @returns an allocated string if `tv` represents a VimL string, list, or /// number; NULL otherwise. -char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl) +char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl, bool crlf) FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL { *len = 0; @@ -5980,20 +5981,23 @@ char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl) // Pre-calculate the resulting length. list_T *list = tv->vval.v_list; TV_LIST_ITER_CONST(list, li, { - *len += (ptrdiff_t)strlen(tv_get_string(TV_LIST_ITEM_TV(li))) + 1; + *len += (ptrdiff_t)strlen(tv_get_string(TV_LIST_ITEM_TV(li))) + (crlf ? 2 : 1); }); if (*len == 0) { return NULL; } - char *ret = xmalloc((size_t)(*len) + endnl); + char *ret = xmalloc((size_t)(*len) + (endnl ? (crlf ? 2 : 1) : 0)); char *end = ret; TV_LIST_ITER_CONST(list, li, { for (const char *s = tv_get_string(TV_LIST_ITEM_TV(li)); *s != NUL; s++) { *end++ = (*s == '\n') ? NUL : *s; } if (endnl || TV_LIST_ITEM_NEXT(list, li) != NULL) { + if (crlf) { + *end++ = '\r'; + } *end++ = '\n'; } }); diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 017e8e502c..8bdb91ebf1 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -648,6 +648,14 @@ static void f_chansend(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) ptrdiff_t input_len = 0; char *input = NULL; + uint64_t id = (uint64_t)argvars[0].vval.v_number; +#ifdef UNIX + bool crlf = false; +#else + Channel *chan = find_channel(id); + bool crlf = (chan != NULL && chan->term) ? true: false; +#endif + if (argvars[1].v_type == VAR_BLOB) { const blob_T *const b = argvars[1].vval.v_blob; input_len = tv_blob_len(b); @@ -655,7 +663,7 @@ static void f_chansend(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) input = xmemdup(b->bv_ga.ga_data, (size_t)input_len); } } else { - input = save_tv_as_string(&argvars[1], &input_len, false); + input = save_tv_as_string(&argvars[1], &input_len, false, crlf); } if (!input) { @@ -663,7 +671,6 @@ static void f_chansend(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) // or there is no input to send. return; } - uint64_t id = (uint64_t)argvars[0].vval.v_number; const char *error = NULL; rettv->vval.v_number = (varnumber_T)channel_send(id, input, (size_t)input_len, true, &error); if (error) { diff --git a/test/functional/terminal/channel_spec.lua b/test/functional/terminal/channel_spec.lua index b5f3c2bd31..2ca7cdb0a2 100644 --- a/test/functional/terminal/channel_spec.lua +++ b/test/functional/terminal/channel_spec.lua @@ -7,6 +7,7 @@ local command = helpers.command local pcall_err = helpers.pcall_err local feed = helpers.feed local poke_eventloop = helpers.poke_eventloop +local is_os = helpers.is_os describe('terminal channel is closed and later released if', function() local screen @@ -92,3 +93,17 @@ describe('terminal channel is closed and later released if', function() eq(chans - 1, eval('len(nvim_list_chans())')) end) end) + +it('chansend sends lines to terminal channel in proper order', function() + clear() + local screen = Screen.new(100, 20) + screen:attach() + local shells = is_os('win') and {'cmd.exe', 'pwsh.exe -nop', 'powershell.exe -nop'} or {'sh'} + for _, sh in ipairs(shells) do + command([[bdelete! | let id = termopen(']] .. sh .. [[')]]) + command([[call chansend(id, ['echo "hello"', 'echo "world"', ''])]]) + screen:expect{ + any=[[echo "hello".*echo "world"]] + } + end +end) |