mirror of
https://github.com/neovim/neovim.git
synced 2025-09-07 11:58:17 +00:00
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
This commit is contained in:

committed by
Justin M. Keyes

parent
9b2c790344
commit
d5004aab27
@@ -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
|
// get input to the shell command (if any), and its length
|
||||||
ptrdiff_t input_len;
|
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) {
|
if (input_len < 0) {
|
||||||
assert(input == NULL);
|
assert(input == NULL);
|
||||||
return;
|
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[in] tv Value to store as a string.
|
||||||
/// @param[out] len Length of the resulting string or -1 on error.
|
/// @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] 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
|
/// @returns an allocated string if `tv` represents a VimL string, list, or
|
||||||
/// number; NULL otherwise.
|
/// 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
|
FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL
|
||||||
{
|
{
|
||||||
*len = 0;
|
*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.
|
// Pre-calculate the resulting length.
|
||||||
list_T *list = tv->vval.v_list;
|
list_T *list = tv->vval.v_list;
|
||||||
TV_LIST_ITER_CONST(list, li, {
|
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) {
|
if (*len == 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *ret = xmalloc((size_t)(*len) + endnl);
|
char *ret = xmalloc((size_t)(*len) + (endnl ? (crlf ? 2 : 1) : 0));
|
||||||
char *end = ret;
|
char *end = ret;
|
||||||
TV_LIST_ITER_CONST(list, li, {
|
TV_LIST_ITER_CONST(list, li, {
|
||||||
for (const char *s = tv_get_string(TV_LIST_ITEM_TV(li)); *s != NUL; s++) {
|
for (const char *s = tv_get_string(TV_LIST_ITEM_TV(li)); *s != NUL; s++) {
|
||||||
*end++ = (*s == '\n') ? NUL : *s;
|
*end++ = (*s == '\n') ? NUL : *s;
|
||||||
}
|
}
|
||||||
if (endnl || TV_LIST_ITEM_NEXT(list, li) != NULL) {
|
if (endnl || TV_LIST_ITEM_NEXT(list, li) != NULL) {
|
||||||
|
if (crlf) {
|
||||||
|
*end++ = '\r';
|
||||||
|
}
|
||||||
*end++ = '\n';
|
*end++ = '\n';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@@ -648,6 +648,14 @@ static void f_chansend(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
|||||||
|
|
||||||
ptrdiff_t input_len = 0;
|
ptrdiff_t input_len = 0;
|
||||||
char *input = NULL;
|
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) {
|
if (argvars[1].v_type == VAR_BLOB) {
|
||||||
const blob_T *const b = argvars[1].vval.v_blob;
|
const blob_T *const b = argvars[1].vval.v_blob;
|
||||||
input_len = tv_blob_len(b);
|
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);
|
input = xmemdup(b->bv_ga.ga_data, (size_t)input_len);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
input = save_tv_as_string(&argvars[1], &input_len, false);
|
input = save_tv_as_string(&argvars[1], &input_len, false, crlf);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!input) {
|
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.
|
// or there is no input to send.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
uint64_t id = (uint64_t)argvars[0].vval.v_number;
|
|
||||||
const char *error = NULL;
|
const char *error = NULL;
|
||||||
rettv->vval.v_number = (varnumber_T)channel_send(id, input, (size_t)input_len, true, &error);
|
rettv->vval.v_number = (varnumber_T)channel_send(id, input, (size_t)input_len, true, &error);
|
||||||
if (error) {
|
if (error) {
|
||||||
|
@@ -7,6 +7,7 @@ local command = helpers.command
|
|||||||
local pcall_err = helpers.pcall_err
|
local pcall_err = helpers.pcall_err
|
||||||
local feed = helpers.feed
|
local feed = helpers.feed
|
||||||
local poke_eventloop = helpers.poke_eventloop
|
local poke_eventloop = helpers.poke_eventloop
|
||||||
|
local iswin = helpers.iswin
|
||||||
|
|
||||||
describe('terminal channel is closed and later released if', function()
|
describe('terminal channel is closed and later released if', function()
|
||||||
local screen
|
local screen
|
||||||
@@ -92,3 +93,17 @@ describe('terminal channel is closed and later released if', function()
|
|||||||
eq(chans - 1, eval('len(nvim_list_chans())'))
|
eq(chans - 1, eval('len(nvim_list_chans())'))
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('chansend sends lines to terminal channel in proper order', function()
|
||||||
|
clear()
|
||||||
|
local screen = Screen.new(100, 20)
|
||||||
|
screen:attach()
|
||||||
|
local shells = iswin() 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)
|
||||||
|
Reference in New Issue
Block a user