From 4b1f21de75f9981007d80aca8355239e8615d6bd Mon Sep 17 00:00:00 2001 From: erw7 Date: Tue, 28 Mar 2017 18:07:58 +0900 Subject: [PATCH] win: support :terminal --- src/nvim/CMakeLists.txt | 1 + src/nvim/os/pty_process_win.c | 244 +++++++++++++----- src/nvim/os/pty_process_win.h | 13 +- test/functional/fixtures/tty-test.c | 44 ++-- test/functional/terminal/buffer_spec.lua | 6 +- test/functional/terminal/cursor_spec.lua | 2 - test/functional/terminal/ex_terminal_spec.lua | 11 +- test/functional/terminal/helpers.lua | 7 +- test/functional/terminal/highlight_spec.lua | 5 +- test/functional/terminal/mouse_spec.lua | 5 +- test/functional/terminal/scrollback_spec.lua | 64 ++++- test/functional/terminal/window_spec.lua | 2 - 12 files changed, 290 insertions(+), 114 deletions(-) diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 9bb3fbfcbe..2e0a35d4ab 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -436,6 +436,7 @@ if(WIN32) COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/Qt5Network.dll" ${PROJECT_BINARY_DIR}/windows_runtime_deps/ COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/Qt5Svg.dll" ${PROJECT_BINARY_DIR}/windows_runtime_deps/ COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/Qt5Widgets.dll" ${PROJECT_BINARY_DIR}/windows_runtime_deps/ + COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/winpty.dll" ${PROJECT_BINARY_DIR}/windows_runtime_deps/ COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/platforms/qwindows.dll" ${PROJECT_BINARY_DIR}/windows_runtime_deps/platforms/ ) diff --git a/src/nvim/os/pty_process_win.c b/src/nvim/os/pty_process_win.c index e75c92e7fb..ea95c1bb09 100644 --- a/src/nvim/os/pty_process_win.c +++ b/src/nvim/os/pty_process_win.c @@ -2,30 +2,53 @@ #include #include +#include "nvim/vim.h" +#include "nvim/ascii.h" #include "nvim/memory.h" +#include "nvim/mbyte.h" // for utf8_to_utf16, utf16_to_utf8 #include "nvim/os/pty_process_win.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "os/pty_process_win.c.generated.h" #endif -static void CALLBACK pty_process_finish1(void *context, BOOLEAN unused) +static void wait_eof_timer_cb(uv_timer_t *wait_eof_timer) + FUNC_ATTR_NONNULL_ALL { - uv_async_t *finish_async = (uv_async_t *)context; - uv_async_send(finish_async); + PtyProcess *ptyproc = + (PtyProcess *)((uv_handle_t *)wait_eof_timer->data); + Process *proc = (Process *)ptyproc; + + if (!uv_is_readable(proc->out->uvstream)) { + uv_timer_stop(&ptyproc->wait_eof_timer); + pty_process_finish2(ptyproc); + } } -bool pty_process_spawn(PtyProcess *ptyproc) +static void CALLBACK pty_process_finish1(void *context, BOOLEAN unused) + FUNC_ATTR_NONNULL_ALL +{ + PtyProcess *ptyproc = (PtyProcess *)context; + Process *proc = (Process *)ptyproc; + + uv_timer_init(&proc->loop->uv, &ptyproc->wait_eof_timer); + ptyproc->wait_eof_timer.data = (void *)ptyproc; + uv_timer_start(&ptyproc->wait_eof_timer, wait_eof_timer_cb, 200, 200); +} + +int pty_process_spawn(PtyProcess *ptyproc) FUNC_ATTR_NONNULL_ALL { Process *proc = (Process *)ptyproc; - bool success = false; + int status = 0; winpty_error_ptr_t err = NULL; winpty_config_t *cfg = NULL; winpty_spawn_config_t *spawncfg = NULL; winpty_t *wp = NULL; char *in_name = NULL, *out_name = NULL; HANDLE process_handle = NULL; + uv_connect_t *in_req = NULL, *out_req = NULL; + wchar_t *cmdline = NULL, *cwd = NULL; assert(proc->in && proc->out && !proc->err); @@ -33,52 +56,70 @@ bool pty_process_spawn(PtyProcess *ptyproc) WINPTY_FLAG_ALLOW_CURPROC_DESKTOP_CREATION, &err))) { goto cleanup; } - winpty_config_set_initial_size(cfg, ptyproc->width, ptyproc->height); + winpty_config_set_initial_size( + cfg, + ptyproc->width, + ptyproc->height); if (!(wp = winpty_open(cfg, &err))) { goto cleanup; } - in_name = utf16_to_utf8(winpty_conin_name(wp)); - out_name = utf16_to_utf8(winpty_conout_name(wp)); + if ((status = utf16_to_utf8(winpty_conin_name(wp), &in_name))) { + goto cleanup; + } + if ((status = utf16_to_utf8(winpty_conout_name(wp), &out_name))) { + goto cleanup; + } + in_req = xmalloc(sizeof(uv_connect_t)); + out_req = xmalloc(sizeof(uv_connect_t)); uv_pipe_connect( - xmalloc(sizeof(uv_connect_t)), + in_req, &proc->in->uv.pipe, in_name, pty_process_connect_cb); uv_pipe_connect( - xmalloc(sizeof(uv_connect_t)), + out_req, &proc->out->uv.pipe, out_name, pty_process_connect_cb); - // XXX: Provide the correct ptyprocess parameters (at least, the cmdline... - // probably cwd too? what about environ?) + if (proc->cwd != NULL && (status = utf8_to_utf16(proc->cwd, &cwd))) { + goto cleanup; + } + if ((status = build_cmdline(proc->argv, &cmdline))) { + goto cleanup; + } if (!(spawncfg = winpty_spawn_config_new( WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN, - L"C:\\Windows\\System32\\cmd.exe", - L"C:\\Windows\\System32\\cmd.exe", - NULL, NULL, - &err))) { + NULL, cmdline, cwd, NULL, &err))) { goto cleanup; } if (!winpty_spawn(wp, spawncfg, &process_handle, NULL, NULL, &err)) { goto cleanup; } + proc->pid = GetProcessId(process_handle); - uv_async_init(&proc->loop->uv, &ptyproc->finish_async, pty_process_finish2); - if (!RegisterWaitForSingleObject(&ptyproc->finish_wait, process_handle, - pty_process_finish1, &ptyproc->finish_async, INFINITE, 0)) { + if (!RegisterWaitForSingleObject( + &ptyproc->finish_wait, + process_handle, pty_process_finish1, ptyproc, + INFINITE, WT_EXECUTEDEFAULT | WT_EXECUTEONLYONCE)) { abort(); } + while (in_req->handle || out_req->handle) { + uv_run(&proc->loop->uv, UV_RUN_ONCE); + } + ptyproc->wp = wp; ptyproc->process_handle = process_handle; wp = NULL; process_handle = NULL; - success = true; cleanup: + if (err != NULL) { + status = (int)winpty_error_code(err); + } winpty_error_free(err); winpty_config_free(cfg); winpty_spawn_config_free(spawncfg); @@ -88,7 +129,11 @@ cleanup: if (process_handle != NULL) { CloseHandle(process_handle); } - return success; + xfree(in_req); + xfree(out_req); + xfree(cmdline); + xfree(cwd); + return status; } void pty_process_resize(PtyProcess *ptyproc, uint16_t width, @@ -105,17 +150,10 @@ void pty_process_close(PtyProcess *ptyproc) { Process *proc = (Process *)ptyproc; - ptyproc->is_closing = true; pty_process_close_master(ptyproc); - uv_handle_t *finish_async_handle = (uv_handle_t *)&ptyproc->finish_async; - if (ptyproc->finish_wait != NULL) { - // Use INVALID_HANDLE_VALUE to block until either the wait is cancelled - // or the callback has signalled the uv_async_t. - UnregisterWaitEx(ptyproc->finish_wait, INVALID_HANDLE_VALUE); - uv_close(finish_async_handle, pty_process_finish_closing); - } else { - pty_process_finish_closing(finish_async_handle); + if (proc->internal_close_cb) { + proc->internal_close_cb(proc); } } @@ -133,57 +171,123 @@ void pty_process_teardown(Loop *loop) { } -// Returns a string freeable with xfree. Never returns NULL (OOM is a fatal -// error). Windows appears to replace invalid UTF-16 code points (i.e. -// unpaired surrogates) using U+FFFD (the replacement character). -static char *utf16_to_utf8(LPCWSTR str) +static void pty_process_connect_cb(uv_connect_t *req, int status) FUNC_ATTR_NONNULL_ALL { - int len = WideCharToMultiByte(CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL); - assert(len >= 1); // Even L"" has a non-zero length due to NUL terminator. - char *ret = xmalloc(len); - int len2 = WideCharToMultiByte(CP_UTF8, 0, str, -1, ret, len, NULL, NULL); - assert(len == len2); + assert(status == 0); + req->handle = NULL; +} + +static void pty_process_finish2(PtyProcess *ptyproc) + FUNC_ATTR_NONNULL_ALL +{ + Process *proc = (Process *)ptyproc; + + UnregisterWaitEx(ptyproc->finish_wait, NULL); + uv_close((uv_handle_t *)&ptyproc->wait_eof_timer, NULL); + + DWORD exit_code = 0; + GetExitCodeProcess(ptyproc->process_handle, &exit_code); + proc->status = (int)exit_code; + + CloseHandle(ptyproc->process_handle); + ptyproc->process_handle = NULL; + + proc->internal_exit_cb(proc); +} + +static int build_cmdline(char **argv, wchar_t **cmdline) + FUNC_ATTR_NONNULL_ALL +{ + char *args = NULL; + size_t args_len = 0, argc = 0; + int ret; + QUEUE q; + QUEUE_INIT(&q); + + while (*argv) { + arg_T *arg = xmalloc(sizeof(arg_T)); + arg->arg = (char *)xmalloc(strlen(*argv) * 2 + 3); + quote_cmd_arg(arg->arg, *argv); + args_len += strlen(arg->arg); + QUEUE_INIT(&arg->node); + QUEUE_INSERT_TAIL(&q, &arg->node); + argc++; + argv++; + } + args_len += argc; + args = xmalloc(args_len); + *args = NUL; + while (1) { + QUEUE *head = QUEUE_HEAD(&q); + QUEUE_REMOVE(head); + arg_T *arg = QUEUE_DATA(head, arg_T, node); + xstrlcat(args, arg->arg, args_len); + xfree(arg->arg); + xfree(arg); + if (QUEUE_EMPTY(&q)) { + break; + } else { + xstrlcat(args, " ", args_len); + } + } + ret = utf8_to_utf16(args, cmdline); + xfree(args); return ret; } -static void pty_process_connect_cb(uv_connect_t *req, int status) +// Emulate quote_cmd_arg of libuv and quotes command line arguments +static void quote_cmd_arg(char *target, const char *source) + FUNC_ATTR_NONNULL_ALL { - assert(status == 0); - xfree(req); -} + size_t len = strlen(source); + size_t i; + bool quote_hit = true; + char *start = target; + char tmp; -static void pty_process_finish2(uv_async_t *finish_async) -{ - PtyProcess *ptyproc = - (PtyProcess *)((char *)finish_async - offsetof(PtyProcess, finish_async)); - Process *proc = (Process *)ptyproc; + if (len == 0) { + *(target++) = '"'; + *(target++) = '"'; + *target = NUL; + return; + } - if (!ptyproc->is_closing) { - // If pty_process_close has already been called, be consistent and never - // call the internal_exit callback. + if (NULL == strpbrk(source, " \t\"")) { + strcpy(target, source); + return; + } - DWORD exit_code = 0; - GetExitCodeProcess(ptyproc->process_handle, &exit_code); - proc->status = exit_code; + if (NULL == strpbrk(source, "\"\\")) { + *(target++) = '"'; + strncpy(target, source, len); + target += len; + *(target++) = '"'; + *target = NUL; + return; + } - if (proc->internal_exit_cb) { - proc->internal_exit_cb(proc); + *(target++) = NUL; + *(target++) = '"'; + for (i = len; i > 0; --i) { + *(target++) = source[i - 1]; + + if (quote_hit && source[i - 1] == '\\') { + *(target++) = '\\'; + } else if (source[i - 1] == '"') { + quote_hit = true; + *(target++) = '\\'; + } else { + quote_hit = false; } } -} - -static void pty_process_finish_closing(uv_handle_t *finish_async) -{ - PtyProcess *ptyproc = - (PtyProcess *)((char *)finish_async - offsetof(PtyProcess, finish_async)); - Process *proc = (Process *)ptyproc; - - if (ptyproc->process_handle != NULL) { - CloseHandle(ptyproc->process_handle); - ptyproc->process_handle = NULL; - } - if (proc->internal_close_cb) { - proc->internal_close_cb(proc); + *target = '"'; + while (start < target) { + tmp = *start; + *start = *target; + *target = tmp; + start++; + target--; } + return; } diff --git a/src/nvim/os/pty_process_win.h b/src/nvim/os/pty_process_win.h index 87b2b6545d..806857f130 100644 --- a/src/nvim/os/pty_process_win.h +++ b/src/nvim/os/pty_process_win.h @@ -5,19 +5,24 @@ #include -#include "nvim/event/libuv_process.h" +#include "nvim/event/process.h" +#include "nvim/lib/queue.h" typedef struct pty_process { Process process; char *term_name; uint16_t width, height; winpty_t *wp; - uv_async_t finish_async; HANDLE finish_wait; HANDLE process_handle; - bool is_closing; + uv_timer_t wait_eof_timer; } PtyProcess; +typedef struct arg_S { + char *arg; + QUEUE node; +} arg_T; + static inline PtyProcess pty_process_init(Loop *loop, void *data) { PtyProcess rv; @@ -26,10 +31,8 @@ static inline PtyProcess pty_process_init(Loop *loop, void *data) rv.width = 80; rv.height = 24; rv.wp = NULL; - // XXX: Zero rv.finish_async somehow? rv.finish_wait = NULL; rv.process_handle = NULL; - rv.is_closing = false; return rv; } diff --git a/test/functional/fixtures/tty-test.c b/test/functional/fixtures/tty-test.c index 7ba21d652a..dd94d1a256 100644 --- a/test/functional/fixtures/tty-test.c +++ b/test/functional/fixtures/tty-test.c @@ -15,10 +15,12 @@ uv_tty_t tty; #include bool owns_tty(void) { - HWND consoleWnd = GetConsoleWindow(); - DWORD dwProcessId; - GetWindowThreadProcessId(consoleWnd, &dwProcessId); - return GetCurrentProcessId() == dwProcessId; + /* XXX: We need to make proper detect owns tty */ + /* HWND consoleWnd = GetConsoleWindow(); */ + /* DWORD dwProcessId; */ + /* GetWindowThreadProcessId(consoleWnd, &dwProcessId); */ + /* return GetCurrentProcessId() == dwProcessId; */ + return true; } #else #include @@ -54,16 +56,18 @@ static void sig_handler(int signum) return; } } +#else +static void sigwinch_cb(uv_signal_t *handle, int signum) +{ + int width, height; + uv_tty_t out; + uv_tty_init(uv_default_loop(), &out, fileno(stdout), 0); + uv_tty_get_winsize(&out, &width, &height); + fprintf(stderr, "rows: %d, cols: %d\n", height, width); + uv_close((uv_handle_t *)&out, NULL); +} #endif -// static void sigwinch_cb(uv_signal_t *handle, int signum) -// { -// int width, height; -// uv_tty_t *tty = handle->data; -// uv_tty_get_winsize(tty, &width, &height); -// fprintf(stderr, "rows: %d, cols: %d\n", height, width); -// } - static void alloc_cb(uv_handle_t *handle, size_t suggested, uv_buf_t *buf) { buf->len = BUF_SIZE; @@ -88,7 +92,7 @@ static void read_cb(uv_stream_t *stream, ssize_t cnt, const uv_buf_t *buf) uv_loop_t write_loop; uv_loop_init(&write_loop); uv_tty_t out; - uv_tty_init(&write_loop, &out, 1, 0); + uv_tty_init(&write_loop, &out, fileno(stdout), 0); uv_write_t req; uv_buf_t b = {.base = buf->base, .len = (size_t)cnt}; uv_write(&req, STRUCT_CAST(uv_stream_t, &out), &b, 1, NULL); @@ -149,7 +153,11 @@ int main(int argc, char **argv) uv_prepare_init(uv_default_loop(), &prepare); uv_prepare_start(&prepare, prepare_cb); // uv_tty_t tty; +#ifndef WIN32 uv_tty_init(uv_default_loop(), &tty, fileno(stderr), 1); +#else + uv_tty_init(uv_default_loop(), &tty, fileno(stdin), 1); +#endif uv_tty_set_mode(&tty, UV_TTY_MODE_RAW); tty.data = &interrupted; uv_read_start(STRUCT_CAST(uv_stream_t, &tty), alloc_cb, read_cb); @@ -160,15 +168,17 @@ int main(int argc, char **argv) sa.sa_handler = sig_handler; sigaction(SIGHUP, &sa, NULL); sigaction(SIGWINCH, &sa, NULL); - // uv_signal_t sigwinch_watcher; - // uv_signal_init(uv_default_loop(), &sigwinch_watcher); - // sigwinch_watcher.data = &tty; - // uv_signal_start(&sigwinch_watcher, sigwinch_cb, SIGWINCH); +#else + uv_signal_t sigwinch_watcher; + uv_signal_init(uv_default_loop(), &sigwinch_watcher); + uv_signal_start(&sigwinch_watcher, sigwinch_cb, SIGWINCH); #endif uv_run(uv_default_loop(), UV_RUN_DEFAULT); +#ifndef WIN32 // XXX: Without this the SIGHUP handler is skipped on some systems. sleep(100); +#endif return 0; } diff --git a/test/functional/terminal/buffer_spec.lua b/test/functional/terminal/buffer_spec.lua index 48b8512bf0..22ab0a8c21 100644 --- a/test/functional/terminal/buffer_spec.lua +++ b/test/functional/terminal/buffer_spec.lua @@ -6,8 +6,6 @@ local eval, feed_command, source = helpers.eval, helpers.feed_command, helpers.s local eq, neq = helpers.eq, helpers.neq local write_file = helpers.write_file -if helpers.pending_win32(pending) then return end - describe('terminal buffer', function() local screen @@ -72,6 +70,7 @@ describe('terminal buffer', function() end) it('cannot be modified directly', function() + if helpers.pending_win32(pending) then return end feed('dd') screen:expect([[ tty ready | @@ -160,6 +159,7 @@ describe('terminal buffer', function() end) it('handles loss of focus gracefully', function() + if helpers.pending_win32(pending) then return end -- Change the statusline to avoid printing the file name, which varies. nvim('set_option', 'statusline', '==========') feed_command('set laststatus=0') @@ -205,7 +205,7 @@ describe('terminal buffer', function() end) describe('No heap-buffer-overflow when using', function() - + if helpers.pending_win32(pending) then return end local testfilename = 'Xtestfile-functional-terminal-buffers_spec' before_each(function() diff --git a/test/functional/terminal/cursor_spec.lua b/test/functional/terminal/cursor_spec.lua index 84d0322f12..d49f1bfc23 100644 --- a/test/functional/terminal/cursor_spec.lua +++ b/test/functional/terminal/cursor_spec.lua @@ -7,8 +7,6 @@ local feed_command = helpers.feed_command local hide_cursor = thelpers.hide_cursor local show_cursor = thelpers.show_cursor -if helpers.pending_win32(pending) then return end - describe('terminal cursor', function() local screen diff --git a/test/functional/terminal/ex_terminal_spec.lua b/test/functional/terminal/ex_terminal_spec.lua index be0fd9f8ff..5eb6a5f18c 100644 --- a/test/functional/terminal/ex_terminal_spec.lua +++ b/test/functional/terminal/ex_terminal_spec.lua @@ -3,10 +3,10 @@ local Screen = require('test.functional.ui.screen') local clear, wait, nvim = helpers.clear, helpers.wait, helpers.nvim local nvim_dir, source, eq = helpers.nvim_dir, helpers.source, helpers.eq local feed_command, eval = helpers.feed_command, helpers.eval - -if helpers.pending_win32(pending) then return end +local iswin = helpers.iswin describe(':terminal', function() + if helpers.pending_win32(pending) then return end local screen before_each(function() @@ -174,10 +174,15 @@ describe(':terminal (with fake shell)', function() eq('term://', string.match(eval('bufname("%")'), "^term://")) helpers.feed([[]]) feed_command([[find */shadacat.py]]) - eq('scripts/shadacat.py', eval('bufname("%")')) + if iswin() then + eq('scripts\\shadacat.py', eval('bufname("%")')) + else + eq('scripts/shadacat.py', eval('bufname("%")')) + end end) it('works with gf', function() + if helpers.pending_win32(pending) then return end terminal_with_fake_shell([[echo "scripts/shadacat.py"]]) wait() screen:expect([[ diff --git a/test/functional/terminal/helpers.lua b/test/functional/terminal/helpers.lua index 3b04d17705..29381ab4f0 100644 --- a/test/functional/terminal/helpers.lua +++ b/test/functional/terminal/helpers.lua @@ -30,10 +30,14 @@ local function clear_attrs() feed_termcode('[0;10m') end -- mouse local function enable_mouse() feed_termcode('[?1002h') end local function disable_mouse() feed_termcode('[?1002l') end +local function wait_sigwinch() + helpers.sleep(1000) + hide_cursor() + show_cursor() +end local default_command = '["'..nvim_dir..'/tty-test'..'"]' - local function screen_setup(extra_rows, command, cols) extra_rows = extra_rows and extra_rows or 0 command = command and command or default_command @@ -112,5 +116,6 @@ return { clear_attrs = clear_attrs, enable_mouse = enable_mouse, disable_mouse = disable_mouse, + wait_sigwinch = wait_sigwinch, screen_setup = screen_setup } diff --git a/test/functional/terminal/highlight_spec.lua b/test/functional/terminal/highlight_spec.lua index bb40770235..fddc0bbb71 100644 --- a/test/functional/terminal/highlight_spec.lua +++ b/test/functional/terminal/highlight_spec.lua @@ -5,8 +5,6 @@ local feed, clear, nvim = helpers.feed, helpers.clear, helpers.nvim local nvim_dir, command = helpers.nvim_dir, helpers.command local eq, eval = helpers.eq, helpers.eval -if helpers.pending_win32(pending) then return end - describe('terminal window highlighting', function() local screen @@ -55,6 +53,7 @@ describe('terminal window highlighting', function() end) local function pass_attrs() + if helpers.pending_win32(pending) then return end screen:expect(sub([[ tty ready | {NUM:text}text{10: } | @@ -69,6 +68,7 @@ describe('terminal window highlighting', function() it('will pass the corresponding attributes', pass_attrs) it('will pass the corresponding attributes on scrollback', function() + if helpers.pending_win32(pending) then return end pass_attrs() local lines = {} for i = 1, 8 do @@ -145,6 +145,7 @@ describe('terminal window highlighting with custom palette', function() end) it('will use the custom color', function() + if helpers.pending_win32(pending) then return end thelpers.set_fg(3) thelpers.feed_data('text') thelpers.clear_attrs() diff --git a/test/functional/terminal/mouse_spec.lua b/test/functional/terminal/mouse_spec.lua index 9239c2ad31..29c62d7be7 100644 --- a/test/functional/terminal/mouse_spec.lua +++ b/test/functional/terminal/mouse_spec.lua @@ -4,8 +4,6 @@ local clear, eq, eval = helpers.clear, helpers.eq, helpers.eval local feed, nvim = helpers.feed, helpers.nvim local feed_data = thelpers.feed_data -if helpers.pending_win32(pending) then return end - describe('terminal mouse', function() local screen @@ -67,6 +65,7 @@ describe('terminal mouse', function() end) it('will forward mouse clicks to the program', function() + if helpers.pending_win32(pending) then return end feed('<1,2>') screen:expect([[ line27 | @@ -80,6 +79,7 @@ describe('terminal mouse', function() end) it('will forward mouse scroll to the program', function() + if helpers.pending_win32(pending) then return end feed('<0,0>') screen:expect([[ line27 | @@ -94,6 +94,7 @@ describe('terminal mouse', function() end) describe('with a split window and other buffer', function() + if helpers.pending_win32(pending) then return end before_each(function() feed(':vsp') screen:expect([[ diff --git a/test/functional/terminal/scrollback_spec.lua b/test/functional/terminal/scrollback_spec.lua index 05f81295c2..649bb4373b 100644 --- a/test/functional/terminal/scrollback_spec.lua +++ b/test/functional/terminal/scrollback_spec.lua @@ -3,6 +3,7 @@ local helpers = require('test.functional.helpers')(after_each) local thelpers = require('test.functional.terminal.helpers') local clear, eq, curbuf = helpers.clear, helpers.eq, helpers.curbuf local feed, nvim_dir, feed_command = helpers.feed, helpers.nvim_dir, helpers.feed_command +local iswin, wait_sigwinch = helpers.iswin, thelpers.wait_sigwinch local eval = helpers.eval local command = helpers.command local wait = helpers.wait @@ -11,8 +12,6 @@ local curbufmeths = helpers.curbufmeths local nvim = helpers.nvim local feed_data = thelpers.feed_data -if helpers.pending_win32(pending) then return end - describe('terminal scrollback', function() local screen @@ -142,6 +141,9 @@ describe('terminal scrollback', function() describe('and the height is decreased by 1', function() local function will_hide_top_line() screen:try_resize(screen._width, screen._height - 1) + if iswin() then + wait_sigwinch() + end screen:expect([[ line2 | line3 | @@ -158,6 +160,9 @@ describe('terminal scrollback', function() before_each(function() will_hide_top_line() screen:try_resize(screen._width, screen._height - 2) + if iswin() then + wait_sigwinch() + end end) it('will hide the top 3 lines', function() @@ -184,9 +189,13 @@ describe('terminal scrollback', function() describe('and the height is decreased by 2', function() before_each(function() screen:try_resize(screen._width, screen._height - 2) + if iswin() then + wait_sigwinch() + end end) local function will_delete_last_two_lines() + if helpers.pending_win32(pending) then return end screen:expect([[ tty ready | rows: 4, cols: 30 | @@ -200,9 +209,13 @@ describe('terminal scrollback', function() it('will delete the last two empty lines', will_delete_last_two_lines) describe('and then decreased by 1', function() + if helpers.pending_win32(pending) then return end before_each(function() will_delete_last_two_lines() screen:try_resize(screen._width, screen._height - 1) + if iswin() then + wait_sigwinch() + end end) it('will delete the last line and hide the first', function() @@ -245,6 +258,9 @@ describe('terminal scrollback', function() {3:-- TERMINAL --} | ]]) screen:try_resize(screen._width, screen._height - 3) + if iswin() then + wait_sigwinch() + end screen:expect([[ line4 | rows: 3, cols: 30 | @@ -257,6 +273,9 @@ describe('terminal scrollback', function() describe('and the height is increased by 1', function() local function pop_then_push() screen:try_resize(screen._width, screen._height + 1) + if iswin() then + wait_sigwinch() + end screen:expect([[ line4 | rows: 3, cols: 30 | @@ -273,6 +292,9 @@ describe('terminal scrollback', function() pop_then_push() eq(8, curbuf('line_count')) screen:try_resize(screen._width, screen._height + 3) + if iswin() then + wait_sigwinch() + end end) local function pop3_then_push1() @@ -303,10 +325,14 @@ describe('terminal scrollback', function() it('will pop 3 lines and then push one back', pop3_then_push1) describe('and then by 4', function() + if helpers.pending_win32(pending) then return end before_each(function() pop3_then_push1() feed('Gi') screen:try_resize(screen._width, screen._height + 4) + if iswin() then + wait_sigwinch() + end end) it('will show all lines and leave a blank one at the end', function() @@ -384,10 +410,20 @@ describe("'scrollback' option", function() end it('set to 0 behaves as 1', function() - local screen = thelpers.screen_setup(nil, "['sh']", 30) + local screen + if iswin() then + screen = thelpers.screen_setup(nil, + "['powershell.exe', '-NoLogo', '-NoProfile', '-NoExit', '-Command', 'function global:prompt {return "..'"$"'.."}']", 30) + else + screen = thelpers.screen_setup(nil, "['sh']", 30) + end curbufmeths.set_option('scrollback', 0) - feed_data('for i in $(seq 1 30); do echo "line$i"; done\n') + if iswin() then + feed_data('for($i=1;$i -le 30;$i++){Write-Host \"line$i\"}\r') + else + feed_data('for i in $(seq 1 30); do echo "line$i"; done\n') + end screen:expect('line30 ', nil, nil, nil, true) retry(nil, nil, function() expect_lines(7) end) @@ -395,7 +431,13 @@ describe("'scrollback' option", function() end) it('deletes lines (only) if necessary', function() - local screen = thelpers.screen_setup(nil, "['sh']", 30) + local screen + if iswin() then + screen = thelpers.screen_setup(nil, + "['powershell.exe', '-NoLogo', '-NoProfile', '-NoExit', '-Command', 'function global:prompt {return "..'"$"'.."}']", 30) + else + screen = thelpers.screen_setup(nil, "['sh']", 30) + end curbufmeths.set_option('scrollback', 200) @@ -403,7 +445,11 @@ describe("'scrollback' option", function() screen:expect('$', nil, nil, nil, true) wait() - feed_data('for i in $(seq 1 30); do echo "line$i"; done\n') + if iswin() then + feed_data('for($i=1;$i -le 30;$i++){Write-Host \"line$i\"}\r') + else + feed_data('for i in $(seq 1 30); do echo "line$i"; done\n') + end screen:expect('line30 ', nil, nil, nil, true) @@ -416,7 +462,11 @@ describe("'scrollback' option", function() -- Terminal job data is received asynchronously, may happen before the -- 'scrollback' option is synchronized with the internal sb_buffer. command('sleep 100m') - feed_data('for i in $(seq 1 40); do echo "line$i"; done\n') + if iswin() then + feed_data('for($i=1;$i -le 40;$i++){Write-Host \"line$i\"}\r') + else + feed_data('for i in $(seq 1 40); do echo "line$i"; done\n') + end screen:expect('line40 ', nil, nil, nil, true) diff --git a/test/functional/terminal/window_spec.lua b/test/functional/terminal/window_spec.lua index 888b1e1328..0f705cfe40 100644 --- a/test/functional/terminal/window_spec.lua +++ b/test/functional/terminal/window_spec.lua @@ -3,8 +3,6 @@ local thelpers = require('test.functional.terminal.helpers') local feed, clear = helpers.feed, helpers.clear local wait = helpers.wait -if helpers.pending_win32(pending) then return end - describe('terminal window', function() local screen