diff options
-rw-r--r-- | .github/workflows/test.yml | 7 | ||||
-rw-r--r-- | CMakeLists.txt | 25 | ||||
-rw-r--r-- | cmake.deps/CMakeLists.txt | 1 | ||||
-rw-r--r-- | cmake.deps/deps.txt | 8 | ||||
-rw-r--r-- | runtime/doc/api.txt | 15 | ||||
-rw-r--r-- | runtime/doc/autocmd.txt | 25 | ||||
-rw-r--r-- | runtime/doc/eval.txt | 43 | ||||
-rw-r--r-- | runtime/doc/news.txt | 6 | ||||
-rw-r--r-- | runtime/doc/pi_gzip.txt | 10 | ||||
-rw-r--r-- | runtime/doc/pi_zip.txt | 7 | ||||
-rw-r--r-- | runtime/doc/provider.txt | 30 | ||||
-rw-r--r-- | runtime/doc/vim_diff.txt | 2 | ||||
-rw-r--r-- | runtime/lua/vim/_editor.lua | 2 | ||||
-rw-r--r-- | runtime/lua/vim/_meta/api.lua | 11 | ||||
-rw-r--r-- | runtime/lua/vim/clipboard/osc52.lua | 38 | ||||
-rw-r--r-- | runtime/plugin/man.lua | 1 | ||||
-rw-r--r-- | src/nvim/api/ui.c | 27 | ||||
-rw-r--r-- | src/nvim/charset.c | 4 | ||||
-rw-r--r-- | src/nvim/drawline.c | 254 | ||||
-rw-r--r-- | src/nvim/eval/funcs.c | 1 | ||||
-rw-r--r-- | src/nvim/grid.c | 34 | ||||
-rw-r--r-- | src/nvim/quickfix.c | 7 | ||||
-rw-r--r-- | src/nvim/spellsuggest.c | 4 | ||||
-rw-r--r-- | src/nvim/tui/input.c | 34 | ||||
-rw-r--r-- | test/functional/core/job_spec.lua | 23 | ||||
-rw-r--r-- | test/functional/lua/vim_spec.lua | 6 | ||||
-rw-r--r-- | test/functional/ui/decorations_spec.lua | 10 |
27 files changed, 391 insertions, 244 deletions
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 096f86219e..1ebb26d5c0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -136,6 +136,13 @@ jobs: - name: Create log dir run: mkdir -p "$LOG_DIR" + # FIXME(dundargoc): this workaround is needed for macos as the python3 + # provider tests suddenly started to become extremely flaky, and this + # removes the flakiness for some reason. + - uses: actions/setup-python@v4 + with: + python-version: '3.11' + - if: ${{ matrix.test != 'unittest' }} name: Set up interpreter packages run: | diff --git a/CMakeLists.txt b/CMakeLists.txt index a7b7ef9c0f..72889283dc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -286,13 +286,18 @@ ExternalProject_Add(uncrustify CMAKE_CACHE_ARGS ${DEPS_CMAKE_CACHE_ARGS} EXCLUDE_FROM_ALL TRUE) -ExternalProject_Add(lua-dev-deps - URL https://github.com/neovim/deps/raw/5a1f71cceb24990a0b15fd9a472a5f549f019248/opt/lua-dev-deps.tar.gz - URL_HASH SHA256=27db2495f5eddc7fc191701ec9b291486853530c6125609d3197d03481e8d5a2 - DOWNLOAD_NO_PROGRESS TRUE - DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/lua-dev-deps - SOURCE_DIR ${DEPS_SHARE_DIR} - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" - EXCLUDE_FROM_ALL TRUE) +option(USE_BUNDLED_BUSTED "Use bundled luarocks" ON) +if(USE_BUNDLED_BUSTED) + ExternalProject_Add(lua-dev-deps + URL https://github.com/neovim/deps/raw/5a1f71cceb24990a0b15fd9a472a5f549f019248/opt/lua-dev-deps.tar.gz + URL_HASH SHA256=27db2495f5eddc7fc191701ec9b291486853530c6125609d3197d03481e8d5a2 + DOWNLOAD_NO_PROGRESS TRUE + DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/lua-dev-deps + SOURCE_DIR ${DEPS_SHARE_DIR} + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + EXCLUDE_FROM_ALL TRUE) +else() + add_custom_target(lua-dev-deps) +endif() diff --git a/cmake.deps/CMakeLists.txt b/cmake.deps/CMakeLists.txt index 6dc6fcdc72..801be6be2b 100644 --- a/cmake.deps/CMakeLists.txt +++ b/cmake.deps/CMakeLists.txt @@ -11,6 +11,7 @@ list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" "${PROJECT_SOURCE_DI include(ExternalProject) include(CheckCCompilerFlag) +include(FindPackageHandleStandardArgs) include(Deps) include(Find) diff --git a/cmake.deps/deps.txt b/cmake.deps/deps.txt index fd7b72346b..4c1502be75 100644 --- a/cmake.deps/deps.txt +++ b/cmake.deps/deps.txt @@ -1,11 +1,11 @@ -LIBUV_URL https://github.com/libuv/libuv/archive/v1.46.0.tar.gz -LIBUV_SHA256 7aa66be3413ae10605e1f5c9ae934504ffe317ef68ea16fdaa83e23905c681bd +LIBUV_URL https://github.com/libuv/libuv/archive/v1.47.0.tar.gz +LIBUV_SHA256 d50af7e6d72526db137e66fad812421c8a1cae09d146b0ec2bb9a22c5f23ba93 MSGPACK_URL https://github.com/msgpack/msgpack-c/releases/download/c-6.0.0/msgpack-c-6.0.0.tar.gz MSGPACK_SHA256 3654f5e2c652dc52e0a993e270bb57d5702b262703f03771c152bba51602aeba -LUAJIT_URL https://github.com/LuaJIT/LuaJIT/archive/0afa1676b2d2aabf1f3101a2692eb0f1e291076a.tar.gz -LUAJIT_SHA256 907f9ba0b64d1a06b6da092537e0d8b6009a34f5e1405fafaa588da7fdcefe59 +LUAJIT_URL https://github.com/LuaJIT/LuaJIT/archive/b94fbfbee9e7dd0979e35aacea7fcdd43905789b.tar.gz +LUAJIT_SHA256 4fe0cf9323c87fdb717d3b8cd81441d2ead21c14842ed61172b3e99a5892043e LUA_URL https://www.lua.org/ftp/lua-5.1.5.tar.gz LUA_SHA256 2640fc56a795f29d28ef15e13c34a47e223960b0240e8cb0a82d9b0738695333 diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index 2f9e8228d2..b38524bd55 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -3597,6 +3597,21 @@ nvim_ui_set_option({name}, {value}) *nvim_ui_set_option()* Attributes: ~ |RPC| only +nvim_ui_term_event({event}, {value}) *nvim_ui_term_event()* + Tells Nvim when a terminal event has occurred. + + The following terminal events are supported: + + • "osc_response": The terminal sent a OSC response sequence to Nvim. The + payload is the received OSC sequence. + + Attributes: ~ + |RPC| only + + Parameters: ~ + • {event} Event name + • {payload} Event payload + nvim_ui_try_resize({width}, {height}) *nvim_ui_try_resize()* TODO: Documentation diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index 4b36a7d4ec..6b698b0868 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -987,12 +987,25 @@ TermClose When a |terminal| job ends. Sets these |v:event| keys: status *TermResponse* -TermResponse After the response to t_RV is received from - the terminal. The value of |v:termresponse| - can be used to do things depending on the - terminal version. May be triggered halfway - through another event (file I/O, a shell - command, or anything else that takes time). +TermResponse When Nvim receives a OSC response from the + terminal. Sets |v:termresponse|. When used + from Lua, the response string is included in + the "data" field of the autocommand callback. + May be triggered halfway through another event + (file I/O, a shell command, or anything else + that takes time). Example: >lua + + -- Query the terminal palette for the RGB value of color 1 + -- (red) using OSC 4 + vim.api.nvim_create_autocmd('TermResponse', { + once = true, + callback = function(args) + local resp = args.data + local r, g, b = resp:match("\x1b%]4;1;rgb:(%w+)/(%w+)/(%w+)") + end, + }) + io.stdout:write("\x1b]4;1;?\x1b\\") +< *TextChanged* TextChanged After a change was made to the text in the current buffer in Normal mode. That is after diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index c41237b862..2223829548 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2318,18 +2318,10 @@ v:t_string Value of |String| type. Read-only. See: |type()| v:t_blob Value of |Blob| type. Read-only. See: |type()| *v:termresponse* *termresponse-variable* -v:termresponse The escape sequence returned by the terminal for the DA - (request primary device attributes) control sequence. It is - set when Vim receives an escape sequence that starts with ESC - [ or CSI and ends in a 'c', with only digits, ';' and '.' in - between. - When this option is set, the TermResponse autocommand event is - fired, so that you can react to the response from the - terminal. - The response from a new xterm is: "<Esc>[ Pp ; Pv ; Pc c". Pp - is the terminal type: 0 for vt100 and 1 for vt220. Pv is the - patch level (since this was introduced in patch 95, it's - always 95 or bigger). Pc is always zero. +v:termresponse The value of the most recent OSC escape sequence received by + Nvim from the terminal. This can be read in a |TermResponse| + event handler after querying the terminal using another escape + sequence. *v:testing* *testing-variable* v:testing Must be set before using `test_garbagecollect_now()`. @@ -4354,6 +4346,33 @@ This is not allowed when the textlock is active: - etc. ============================================================================== +Vim script library *vim-script-library* + +Vim comes bundled with a Vim script library, that can be used by runtime, +script authors. Currently, it only includes very few functions, but it may +grow over time. + + *dist#vim* +The functions make use of the autoloaded prefix "dist#vim". + +The following functions are available: + +dist#vim#IsSafeExecutable(filetype, executable) ~ + +This function takes a filetype and an executable and checks whether it is safe +to execute the given executable. For security reasons users may not want to +have Vim execute random executables or may have forbidden to do so for +specific filetypes by setting the "<filetype>_exec" variable (|plugin_exec|). + +It returns |TRUE| or |FALSE| to indicate whether the plugin should run the given +exectuable. It takes the following arguments: + + argument type ~ + + filetype string + executable string + +============================================================================== Command-line expressions highlighting *expr-highlight* Expressions entered by the user in |i_CTRL-R_=|, |c_CTRL-\_e|, |quote=| are diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index 9ddb1e91b7..ac84f59308 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -205,6 +205,12 @@ The following new APIs and features were added. • Added |vim.base64.encode()| and |vim.base64.decode()| for encoding and decoding strings using Base64 encoding. +• The |TermResponse| autocommand event can be used with |v:termresponse| to + read escape sequence responses from the terminal. + +• A clipboard provider which uses OSC 52 to copy the selection to the system + clipboard is now bundled by default. |clipboard-osc52| + ============================================================================== CHANGED FEATURES *news-changed* diff --git a/runtime/doc/pi_gzip.txt b/runtime/doc/pi_gzip.txt index a709b34180..1aadc103fd 100644 --- a/runtime/doc/pi_gzip.txt +++ b/runtime/doc/pi_gzip.txt @@ -12,9 +12,17 @@ The functionality mentioned here is a |standard-plugin|. This plugin is only available if 'compatible' is not set. You can avoid loading this plugin by setting the "loaded_gzip" variable: > :let loaded_gzip = 1 +< + *g:gzip_exec* + +For security reasons, one may prevent that Vim runs executables automatically +when opening a buffer. This option (default: "1") can be used to prevent +executing the executables command when set to "0": > + :let g:gzip_exec = 0 +< ============================================================================== -1. Autocommands *gzip-autocmd* +2. Autocommands *gzip-autocmd* The plugin installs autocommands to intercept reading and writing of files with these extensions: diff --git a/runtime/doc/pi_zip.txt b/runtime/doc/pi_zip.txt index 343c73839b..a8e1b8df10 100644 --- a/runtime/doc/pi_zip.txt +++ b/runtime/doc/pi_zip.txt @@ -70,6 +70,13 @@ Copyright: Copyright (C) 2005-2015 Charles E Campbell *zip-copyright* extract a file from a zip archive. By default, > let g:zip_extractcmd= g:zip_unzipcmd < + *g:zip_exec* + For security reasons, one may prevent that Vim runs executables + automatically when opening a buffer. This option (default: "1") + can be used to prevent executing the "unzip" command when set to + "0": > + let g:zip_exec=0 +< PREVENTING LOADING~ If for some reason you do not wish to use vim to examine zipped files, diff --git a/runtime/doc/provider.txt b/runtime/doc/provider.txt index 9e70ff8945..1b49ee3a3d 100644 --- a/runtime/doc/provider.txt +++ b/runtime/doc/provider.txt @@ -253,7 +253,35 @@ For Windows WSL, try this g:clipboard definition: \ }, \ 'cache_enabled': 0, \ } - +< + *clipboard-osc52* +Nvim bundles a clipboard provider that allows copying to the system clipboard +using OSC 52. OSC 52 is an Operating System Command control sequence that +writes the copied text to the terminal emulator. If the terminal emulator +supports OSC 52 then it will write the copied text into the system clipboard. + +This is most useful when using Nvim remotely (e.g. via ssh) as Nvim does not +have direct access to the system clipboard in that case. + +Because not all terminal emulators support OSC 52, this provider must be opted +into explicitly by setting the following |g:clipboard| definition: >lua + + vim.g.clipboard = { + name = 'OSC 52', + copy = { + ['+'] = require('vim.clipboard.osc52').copy, + ['*'] = require('vim.clipboard.osc52').copy, + }, + paste = { + ['+'] = require('vim.clipboard.osc52').paste, + ['*'] = require('vim.clipboard.osc52').paste, + }, + } +< +Note that not all terminal emulators support reading from the system clipboard +(and even for those that do, users should be aware of the security +implications), so using OSC 52 for pasting may not be possible. +< ============================================================================== Paste *provider-paste* *paste* diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index 8249179187..efebf46d85 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -567,6 +567,8 @@ Working directory (Vim implemented some of these after Nvim): Autocommands: - Fixed inconsistent behavior in execution of nested autocommands: https://github.com/neovim/neovim/issues/23368 +- |TermResponse| is fired for any OSC sequence received from the terminal, + instead of the Primary Device Attributes response. |v:termresponse| ============================================================================== Missing features *nvim-missing* diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index 0da127b18f..0bdf0c90a5 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -650,7 +650,7 @@ local on_key_cbs = {} ---if on_key() is called without arguments. function vim.on_key(fn, ns_id) if fn == nil and ns_id == nil then - return #on_key_cbs + return vim.tbl_count(on_key_cbs) end vim.validate({ diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua index 6c6e11a0d3..2142a429a2 100644 --- a/runtime/lua/vim/_meta/api.lua +++ b/runtime/lua/vim/_meta/api.lua @@ -2065,6 +2065,17 @@ function vim.api.nvim_ui_set_focus(gained) end --- @param value any function vim.api.nvim_ui_set_option(name, value) end +--- Tells Nvim when a terminal event has occurred. +--- The following terminal events are supported: +--- +--- • "osc_response": The terminal sent a OSC response sequence to Nvim. The +--- payload is the received OSC sequence. +--- +--- +--- @param event string Event name +--- @param value any +function vim.api.nvim_ui_term_event(event, value) end + --- @param width integer --- @param height integer function vim.api.nvim_ui_try_resize(width, height) end diff --git a/runtime/lua/vim/clipboard/osc52.lua b/runtime/lua/vim/clipboard/osc52.lua new file mode 100644 index 0000000000..0e8f9d378f --- /dev/null +++ b/runtime/lua/vim/clipboard/osc52.lua @@ -0,0 +1,38 @@ +local M = {} + +function M.copy(lines) + local s = table.concat(lines, '\n') + io.stdout:write(string.format('\x1b]52;;%s\x1b\\', vim.base64.encode(s))) +end + +function M.paste() + local contents = nil + local id = vim.api.nvim_create_autocmd('TermResponse', { + callback = function(args) + local resp = args.data ---@type string + local encoded = resp:match('\x1b%]52;%w?;([A-Za-z0-9+/=]*)') + if encoded then + contents = vim.base64.decode(encoded) + return true + end + end, + }) + + io.stdout:write('\x1b]52;;?\x1b\\') + + vim.wait(1000, function() + return contents ~= nil + end) + + -- Delete the autocommand if it didn't already delete itself + pcall(vim.api.nvim_del_autocmd, id) + + if contents then + return vim.split(contents, '\n') + end + + vim.notify('Timed out waiting for a clipboard response from the terminal', vim.log.levels.WARN) + return 0 +end + +return M diff --git a/runtime/plugin/man.lua b/runtime/plugin/man.lua index 4b1528b0cb..50a48fe7f2 100644 --- a/runtime/plugin/man.lua +++ b/runtime/plugin/man.lua @@ -16,6 +16,7 @@ vim.api.nvim_create_user_command('Man', function(params) end, { bang = true, bar = true, + range = true, addr = 'other', nargs = '*', complete = function(...) diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index b508a3ee94..99215f7b4f 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -16,6 +16,7 @@ #include "nvim/api/ui.h" #include "nvim/autocmd.h" #include "nvim/channel.h" +#include "nvim/eval.h" #include "nvim/event/loop.h" #include "nvim/event/wstream.h" #include "nvim/globals.h" @@ -524,6 +525,32 @@ void nvim_ui_pum_set_bounds(uint64_t channel_id, Float width, Float height, Floa ui->pum_pos = true; } +/// Tells Nvim when a terminal event has occurred. +/// +/// The following terminal events are supported: +/// +/// - "osc_response": The terminal sent a OSC response sequence to Nvim. The +/// payload is the received OSC sequence. +/// +/// @param channel_id +/// @param event Event name +/// @param payload Event payload +/// @param[out] err Error details, if any. +void nvim_ui_term_event(uint64_t channel_id, String event, Object value, Error *err) + FUNC_API_SINCE(12) FUNC_API_REMOTE_ONLY +{ + if (strequal("osc_response", event.data)) { + if (value.type != kObjectTypeString) { + api_set_error(err, kErrorTypeValidation, "osc_response must be a string"); + return; + } + + const String osc_response = value.data.string; + set_vim_var_string(VV_TERMRESPONSE, osc_response.data, (ptrdiff_t)osc_response.size); + apply_autocmds_group(EVENT_TERMRESPONSE, NULL, NULL, false, AUGROUP_ALL, NULL, NULL, &value); + } +} + static void flush_event(UIData *data) { if (data->cur_event) { diff --git a/src/nvim/charset.c b/src/nvim/charset.c index eb858b8d5e..95229c5ffb 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -650,9 +650,9 @@ size_t transchar_hex(char *const buf, const int c) /// Mirror text "str" for right-left displaying. /// Only works for single-byte characters (e.g., numbers). -void rl_mirror_ascii(char *str) +void rl_mirror_ascii(char *str, char *end) { - for (char *p1 = str, *p2 = str + strlen(str) - 1; p1 < p2; p1++, p2--) { + for (char *p1 = str, *p2 = (end ? end : str + strlen(str)) - 1; p1 < p2; p1++, p2--) { char t = *p1; *p1 = *p2; *p2 = t; diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index a28b6a8aa1..c353583dfa 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -201,20 +201,13 @@ static void margin_columns_win(win_T *wp, int *left_col, int *right_col) prev_col_off = cur_col_off; } -/// If one half of a double-width char will be overwritten, -/// change the other half to a space so that grid redraws properly. -static void line_check_overwrite(schar_T *dest, int cells, int maxcells, bool rl) -{ - assert(cells > 0); - if (cells < maxcells && dest[rl ? -cells + 1 : cells] == 0) { - dest[rl ? -cells : cells] = schar_from_ascii(' '); - } -} - /// Put a single char from an UTF-8 buffer into a line buffer. /// +/// If `*pp` is a double-width char and only one cell is left, emit a space, +/// and don't advance *pp +/// /// Handles composing chars -static int line_putchar(buf_T *buf, const char **pp, schar_T *dest, int maxcells, bool rl, int vcol) +static int line_putchar(buf_T *buf, const char **pp, schar_T *dest, int maxcells, int vcol) { const char *p = *pp; int cells = utf_ptr2cells(p); @@ -222,17 +215,21 @@ static int line_putchar(buf_T *buf, const char **pp, schar_T *dest, int maxcells int u8c, u8cc[MAX_MCO]; assert(maxcells > 0); if (cells > maxcells) { - return -1; + dest[0] = schar_from_ascii(' '); + return 1; } + u8c = utfc_ptr2char(p, u8cc); if (*p == TAB) { cells = MIN(tabstop_padding(vcol, buf->b_p_ts, buf->b_p_vts_array), maxcells); } - line_check_overwrite(dest, cells, maxcells, rl); + if (cells < maxcells && dest[cells] == 0) { + dest[cells] = schar_from_ascii(' '); + } if (*p == TAB) { for (int c = 0; c < cells; c++) { - dest[rl ? -c : c] = schar_from_ascii(' '); + dest[c] = schar_from_ascii(' '); } goto done; } else if ((uint8_t)(*p) < 0x80 && u8cc[0] == 0) { @@ -241,7 +238,7 @@ static int line_putchar(buf_T *buf, const char **pp, schar_T *dest, int maxcells dest[0] = schar_from_cc(u8c, u8cc); } if (cells > 1) { - dest[rl ? -1 : 1] = 0; + dest[1] = 0; } done: *pp += c_len; @@ -251,7 +248,7 @@ done: static void draw_virt_text(win_T *wp, buf_T *buf, int col_off, int *end_col, int win_row) { DecorState *state = &decor_state; - const int max_col = wp->w_p_rl ? -1 : wp->w_grid.cols; + const int max_col = wp->w_grid.cols; int right_pos = max_col; bool do_eol = state->eol_col > -1; for (size_t i = 0; i < kv_size(state->active); i++) { @@ -262,20 +259,12 @@ static void draw_virt_text(win_T *wp, buf_T *buf, int col_off, int *end_col, int if (item->draw_col == -1) { bool updated = true; if (item->decor.virt_text_pos == kVTRightAlign) { - if (wp->w_p_rl) { - right_pos += item->decor.virt_text_width; - } else { - right_pos -= item->decor.virt_text_width; - } + right_pos -= item->decor.virt_text_width; item->draw_col = right_pos; } else if (item->decor.virt_text_pos == kVTEndOfLine && do_eol) { item->draw_col = state->eol_col; } else if (item->decor.virt_text_pos == kVTWinCol) { - if (wp->w_p_rl) { - item->draw_col = MIN(col_off - item->decor.col, wp->w_grid.cols - 1); - } else { - item->draw_col = MAX(col_off + item->decor.col, 0); - } + item->draw_col = MAX(col_off + item->decor.col, 0); } else { updated = false; } @@ -295,35 +284,27 @@ static void draw_virt_text(win_T *wp, buf_T *buf, int col_off, int *end_col, int kv_push(win_extmark_arr, m); } if (kv_size(item->decor.virt_text)) { - int vcol = wp->w_p_rl ? col_off - item->draw_col : item->draw_col - col_off; + int vcol = item->draw_col - col_off; col = draw_virt_text_item(buf, item->draw_col, item->decor.virt_text, - item->decor.hl_mode, max_col, vcol, wp->w_p_rl); + item->decor.hl_mode, max_col, vcol); } item->draw_col = INT_MIN; // deactivate if (item->decor.virt_text_pos == kVTEndOfLine && do_eol) { - if (wp->w_p_rl) { - state->eol_col = col - 1; - } else { - state->eol_col = col + 1; - } + state->eol_col = col + 1; } - if (wp->w_p_rl) { - *end_col = MIN(*end_col, col); - } else { - *end_col = MAX(*end_col, col); - } + *end_col = MAX(*end_col, col); } } static int draw_virt_text_item(buf_T *buf, int col, VirtText vt, HlMode hl_mode, int max_col, - int vcol, bool rl) + int vcol) { const char *p = ""; int virt_attr = 0; size_t virt_pos = 0; - while (rl ? col > max_col : col < max_col) { + while (col < max_col) { if (!*p) { if (virt_pos >= kv_size(vt)) { break; @@ -348,23 +329,12 @@ static int draw_virt_text_item(buf_T *buf, int col, VirtText vt, HlMode hl_mode, attr = virt_attr; } schar_T dummy[2]; - int maxcells = rl ? col - max_col : max_col - col; + int maxcells = max_col - col; int cells = line_putchar(buf, &p, through ? dummy : &linebuf_char[col], - maxcells, rl, vcol); - // If we failed to emit a char, we still need to put a space and advance. - if (cells < 1) { - assert(!through); - cells = 1; - line_check_overwrite(&linebuf_char[col], cells, maxcells, rl); - linebuf_char[col] = schar_from_ascii(' '); - } + maxcells, vcol); for (int c = 0; c < cells; c++) { linebuf_attr[col] = attr; - if (rl) { - col--; - } else { - col++; - } + col++; } vcol += cells; } @@ -642,15 +612,9 @@ static void handle_lnum_col(win_T *wp, winlinevars_T *wlv, int num_signs, int si *wlv->p_extra = '-'; } } - if (wp->w_p_rl) { // reverse line numbers - // like rl_mirror_ascii(), but keep the space at the end - char *p2 = skipwhite(wlv->extra); - p2 = skiptowhite(p2) - 1; - for (char *p1 = skipwhite(wlv->extra); p1 < p2; p1++, p2--) { - const char t = *p1; - *p1 = *p2; - *p2 = t; - } + if (wp->w_p_rl) { // reverse line numbers + char *num = skipwhite(wlv->extra); + rl_mirror_ascii(num, skiptowhite(num)); } wlv->p_extra = wlv->extra; wlv->c_extra = NUL; @@ -808,11 +772,7 @@ static void handle_showbreak_and_filler(win_T *wp, winlinevars_T *wlv) // clear-to-end-of-line. wlv->c_extra = ' '; wlv->c_final = NUL; - if (wp->w_p_rl) { - wlv->n_extra = wlv->col + 1; - } else { - wlv->n_extra = wp->w_grid.cols - wlv->col; - } + wlv->n_extra = wp->w_grid.cols - wlv->col; wlv->char_attr = 0; } else if (wlv->filler_todo > 0) { // Draw "deleted" diff line(s) @@ -823,11 +783,7 @@ static void handle_showbreak_and_filler(win_T *wp, winlinevars_T *wlv) wlv->c_extra = wp->w_p_fcs_chars.diff; wlv->c_final = NUL; } - if (wp->w_p_rl) { - wlv->n_extra = wlv->col + 1; - } else { - wlv->n_extra = wp->w_grid.cols - wlv->col; - } + wlv->n_extra = wp->w_grid.cols - wlv->col; wlv->char_attr = win_hl_attr(wp, HLF_DED); } @@ -1030,14 +986,6 @@ static void win_line_start(win_T *wp, winlinevars_T *wlv, bool save_extra) wlv->off = 0; wlv->need_lbr = false; - if (wp->w_p_rl) { - // Rightleft window: process the text in the normal direction, but put - // it in linebuf_char[wlv.off] from right to left. Start at the - // rightmost column of the window. - wlv->col = wp->w_grid.cols - 1; - wlv->off += wlv->col; - } - if (save_extra) { // reset the drawing state for the start of a wrapped line wlv->draw_state = WL_START; @@ -1707,9 +1655,6 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl // Skip fold, sign and number states if 'statuscolumn' is set. wlv.draw_state = WL_STC - 1; } - if (virt_line_offset >= 0 && wp->w_p_rl) { - virt_line_offset = wp->w_grid.cols - 1 - virt_line_offset; - } } if (wlv.draw_state == WL_FOLD - 1 && wlv.n_extra == 0) { @@ -1801,7 +1746,8 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl || (number_only && wlv.draw_state > WL_STC)) && wlv.filler_todo <= 0) { draw_virt_text(wp, buf, win_col_offset, &wlv.col, wlv.row); - win_put_linebuf(wp, wlv.row, 0, wlv.col, -grid->cols, bg_attr, false); + // don't clear anything after wlv.col + win_put_linebuf(wp, wlv.row, 0, wlv.col, wlv.col, bg_attr, false); // Pretend we have finished updating the window. Except when // 'cursorcolumn' is set. if (wp->w_p_cuc) { @@ -1978,7 +1924,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl // Fill rest of line with 'fold'. wlv.c_extra = wp->w_p_fcs_chars.fold; wlv.c_final = NUL; - wlv.n_extra = wp->w_p_rl ? (wlv.col + 1) : (grid->cols - wlv.col); + wlv.n_extra = grid->cols - wlv.col; } if (draw_folded && wlv.n_extra != 0 && wlv.col >= grid->cols) { @@ -2020,8 +1966,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl } // If a double-width char doesn't fit display a '>' in the last column. - if ((wp->w_p_rl ? (wlv.col <= 0) : (wlv.col >= grid->cols - 1)) - && utf_char2cells(mb_c) == 2) { + if (wlv.col >= grid->cols - 1 && utf_char2cells(mb_c) == 2) { c = '>'; mb_c = c; mb_l = 1; @@ -2118,7 +2063,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl // Non-BMP character : display as ? or fullwidth ?. transchar_hex(wlv.extra, mb_c); if (wp->w_p_rl) { // reverse - rl_mirror_ascii(wlv.extra); + rl_mirror_ascii(wlv.extra, NULL); } wlv.p_extra = wlv.extra; @@ -2139,8 +2084,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl // If a double-width char doesn't fit display a '>' in the // last column; the character is displayed at the start of the // next line. - if ((wp->w_p_rl ? (wlv.col <= 0) : (wlv.col >= grid->cols - 1)) - && utf_char2cells(mb_c) == 2) { + if (wlv.col >= grid->cols - 1 && utf_char2cells(mb_c) == 2) { c = '>'; mb_c = c; mb_utf8 = false; @@ -2546,7 +2490,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl || ((wlv.fromcol >= 0 || fromcol_prev >= 0) && wlv.tocol > wlv.vcol && VIsual_mode != Ctrl_V - && (wp->w_p_rl ? (wlv.col >= 0) : (wlv.col < grid->cols)) + && wlv.col < grid->cols && !(noinvcur && lnum == wp->w_cursor.lnum && wlv.vcol == wp->w_virtcol))) @@ -2581,7 +2525,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl wlv.n_extra = byte2cells(c) - 1; } if ((dy_flags & DY_UHEX) && wp->w_p_rl) { - rl_mirror_ascii(wlv.p_extra); // reverse "<12>" + rl_mirror_ascii(wlv.p_extra, NULL); // reverse "<12>" } wlv.c_extra = NUL; wlv.c_final = NUL; @@ -2610,7 +2554,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl && virtual_active() && wlv.tocol != MAXCOL && wlv.vcol < wlv.tocol - && (wp->w_p_rl ? (wlv.col >= 0) : (wlv.col < grid->cols))) { + && wlv.col < grid->cols) { c = ' '; ptr--; // put it back at the NUL } @@ -2652,13 +2596,8 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl } wlv.vcol += wlv.n_extra; if (wp->w_p_wrap && wlv.n_extra > 0) { - if (wp->w_p_rl) { - wlv.col -= wlv.n_extra; - wlv.boguscols -= wlv.n_extra; - } else { - wlv.boguscols += wlv.n_extra; - wlv.col += wlv.n_extra; - } + wlv.boguscols += wlv.n_extra; + wlv.col += wlv.n_extra; } wlv.n_extra = 0; wlv.n_attr = 0; @@ -2685,11 +2624,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl && wp == curwin && lnum == wp->w_cursor.lnum && conceal_cursor_line(wp) && (int)wp->w_virtcol <= wlv.vcol + wlv.skip_cells) { - if (wp->w_p_rl) { - wp->w_wcol = grid->cols - wlv.col + wlv.boguscols - 1; - } else { - wp->w_wcol = wlv.col - wlv.boguscols; - } + wp->w_wcol = wlv.col - wlv.boguscols; wp->w_wrow = wlv.row; did_wcol = true; wp->w_valid |= VALID_WCOL|VALID_WROW|VALID_VIRTCOL; @@ -2753,14 +2688,8 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl || prevcol_hl_flag)) { int n = 0; - if (wp->w_p_rl) { - if (wlv.col < 0) { - n = 1; - } - } else { - if (wlv.col >= grid->cols) { - n = -1; - } + if (wlv.col >= grid->cols) { + n = -1; } if (n != 0) { // At the window boundary, highlight the last character @@ -2786,13 +2715,8 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl } linebuf_attr[wlv.off] = eol_attr; linebuf_vcol[wlv.off] = MAXCOL; - if (wp->w_p_rl) { - wlv.col--; - wlv.off--; - } else { - wlv.col++; - wlv.off++; - } + wlv.col++; + wlv.off++; wlv.vcol++; eol_hl_off = 1; } @@ -2827,8 +2751,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl ? 1 : 0); if (has_decor) { - has_virttext = decor_redraw_eol(wp, &decor_state, &wlv.line_attr, - wlv.col + (wp->w_p_rl ? -eol_skip : eol_skip)); + has_virttext = decor_redraw_eol(wp, &decor_state, &wlv.line_attr, wlv.col + eol_skip); } if (((wp->w_p_cuc @@ -2868,12 +2791,10 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl rightmost_vcol = INT_MAX; } - int col_stride = wp->w_p_rl ? -1 : 1; - - while (wp->w_p_rl ? wlv.col >= 0 : wlv.col < grid->cols) { + while (wlv.col < grid->cols) { linebuf_char[wlv.off] = schar_from_ascii(' '); linebuf_vcol[wlv.off] = MAXCOL; - wlv.col += col_stride; + wlv.col++; if (draw_color_col) { draw_color_col = advance_color_col(VCOL_HLC, &color_cols); } @@ -2889,7 +2810,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl col_attr = hl_combine_attr(col_attr, wlv.line_attr); linebuf_attr[wlv.off] = col_attr; - wlv.off += col_stride; + wlv.off++; if (VCOL_HLC >= rightmost_vcol) { break; @@ -2901,22 +2822,19 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl // TODO(bfredl): integrate with the common beyond-the-end-loop if (wp->w_buffer->terminal) { - // terminal buffers may need to highlight beyond the end of the - // logical line - int n = wp->w_p_rl ? -1 : 1; + // terminal buffers may need to highlight beyond the end of the logical line while (wlv.col >= 0 && wlv.col < grid->cols) { linebuf_char[wlv.off] = schar_from_ascii(' '); linebuf_attr[wlv.off] = wlv.vcol >= TERM_ATTRS_MAX ? 0 : term_attrs[wlv.vcol]; linebuf_vcol[wlv.off] = wlv.vcol; - wlv.off += n; - wlv.vcol += n; - wlv.col += n; + wlv.off++; + wlv.vcol++; + wlv.col++; } } if (kv_size(fold_vt) > 0) { - draw_virt_text_item(buf, win_col_offset, fold_vt, kHlModeCombine, - wp->w_p_rl ? -1 : grid->cols, 0, wp->w_p_rl); + draw_virt_text_item(buf, win_col_offset, fold_vt, kHlModeCombine, grid->cols, 0); } draw_virt_text(wp, buf, win_col_offset, &wlv.col, wlv.row); win_put_linebuf(wp, wlv.row, 0, wlv.col, grid->cols, bg_attr, false); @@ -2941,7 +2859,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl && wp->w_p_list && !wp->w_p_wrap && wlv.filler_todo <= 0 - && (wp->w_p_rl ? wlv.col == 0 : wlv.col == grid->cols - 1) + && wlv.col == grid->cols - 1 && !has_fold) { if (has_decor && *ptr == NUL && lcs_eol_one == 0) { // Tricky: there might be a virtual text just _after_ the last char @@ -3000,11 +2918,6 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl // Skip characters that are left of the screen for 'nowrap'. if (wlv.draw_state < WL_LINE || wlv.skip_cells <= 0) { // Store the character. - if (wp->w_p_rl && utf_char2cells(mb_c) > 1) { - // A double-wide character is: put first half in left cell. - wlv.off--; - wlv.col--; - } if (mb_utf8) { linebuf_char[wlv.off] = schar_from_cc(mb_c, u8cc); } else { @@ -3049,20 +2962,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl if (wlv.tocol == wlv.vcol) { wlv.tocol++; } - - if (wp->w_p_rl) { - // now it's time to backup one cell - wlv.off--; - wlv.col--; - } - } - if (wp->w_p_rl) { - wlv.off--; - wlv.col--; - } else { - wlv.off++; - wlv.col++; } + wlv.off++; + wlv.col++; } else if (wp->w_p_cole > 0 && is_concealing) { wlv.skip_cells--; wlv.vcol_off++; @@ -3083,35 +2985,20 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl // of bad columns we have advanced. if (wlv.n_extra > 0) { wlv.vcol += wlv.n_extra; - if (wp->w_p_rl) { - wlv.col -= wlv.n_extra; - wlv.boguscols -= wlv.n_extra; - } else { - wlv.col += wlv.n_extra; - wlv.boguscols += wlv.n_extra; - } + wlv.col += wlv.n_extra; + wlv.boguscols += wlv.n_extra; wlv.n_extra = 0; wlv.n_attr = 0; } if (utf_char2cells(mb_c) > 1) { // Need to fill two screen columns. - if (wp->w_p_rl) { - wlv.boguscols--; - wlv.col--; - } else { - wlv.boguscols++; - wlv.col++; - } - } - - if (wp->w_p_rl) { - wlv.boguscols--; - wlv.col--; - } else { wlv.boguscols++; wlv.col++; } + + wlv.boguscols++; + wlv.col++; } else { if (wlv.n_extra > 0) { wlv.vcol += wlv.n_extra; @@ -3150,8 +3037,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl wlv.char_attr = saved_attr2; } - if (has_decor && wlv.filler_todo <= 0 - && (wp->w_p_rl ? (wlv.col < 0) : (wlv.col >= grid->cols))) { + if (has_decor && wlv.filler_todo <= 0 && wlv.col >= grid->cols) { // At the end of screen line: might need to peek for decorations just after // this position. if (!has_fold && wp->w_p_wrap && wlv.n_extra == 0) { @@ -3167,8 +3053,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl // At end of screen line and there is more to come: Display the line // so far. If there is no more to display it is caught above. - if ((wp->w_p_rl ? (wlv.col < 0) : (wlv.col >= grid->cols)) - && (!has_fold || virt_line_offset >= 0) + if (wlv.col >= grid->cols && (!has_fold || virt_line_offset >= 0) && (wlv.draw_state != WL_LINE || *ptr != NUL || wlv.filler_todo > 0 @@ -3187,7 +3072,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl int draw_col = wlv.col - wlv.boguscols; if (virt_line_offset >= 0) { draw_virt_text_item(buf, virt_line_offset, kv_A(virt_lines, virt_line_index).line, - kHlModeReplace, wp->w_p_rl ? -1 : grid->cols, 0, wp->w_p_rl); + kHlModeReplace, grid->cols, 0); } else if (wlv.filler_todo <= 0) { draw_virt_text(wp, buf, win_col_offset, &draw_col, wlv.row); } @@ -3266,6 +3151,13 @@ static void win_put_linebuf(win_T *wp, int row, int coloff, int endcol, int clea { ScreenGrid *grid = &wp->w_grid; + int start_col = 0; + + if (wp->w_p_rl) { + linebuf_mirror(&start_col, &clear_width, grid->cols); + endcol = grid->cols - 1 - endcol; + } + // Take care of putting "<<<" on the first line for 'smoothscroll'. if (row == 0 && wp->w_skipcol > 0 // do not overwrite the 'showbreak' text with "<<<" @@ -3293,5 +3185,5 @@ static void win_put_linebuf(win_T *wp, int row, int coloff, int endcol, int clea } grid_adjust(&grid, &row, &coloff); - grid_put_linebuf(grid, row, coloff, 0, endcol, clear_width, wp->w_p_rl, bg_attr, wrap); + grid_put_linebuf(grid, row, coloff, start_col, endcol, clear_width, wp->w_p_rl, bg_attr, wrap); } diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 505a91813a..550d296093 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -4187,6 +4187,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) } ui_busy_start(); + ui_flush(); list_T *args = argvars[0].vval.v_list; Channel **jobs = xcalloc((size_t)tv_list_len(args), sizeof(*jobs)); MultiQueue *waiting_jobs = multiqueue_new_parent(loop_on_put, &main_loop); diff --git a/src/nvim/grid.c b/src/nvim/grid.c index d59c3b4803..45bffdcab5 100644 --- a/src/nvim/grid.c +++ b/src/nvim/grid.c @@ -477,14 +477,21 @@ void grid_line_mirror(void) if (grid_line_first >= grid_line_last) { return; } + linebuf_mirror(&grid_line_first, &grid_line_last, grid_line_maxcol); +} + +void linebuf_mirror(int *firstp, int *lastp, int maxcol) +{ + int first = *firstp; + int last = *lastp; - size_t n = (size_t)(grid_line_last - grid_line_first); - int mirror = grid_line_maxcol - 1; // Mirrors are more fun than television. + size_t n = (size_t)(last - first); + int mirror = maxcol - 1; // Mirrors are more fun than television. schar_T *scratch_char = (schar_T *)linebuf_scratch; - memcpy(scratch_char + grid_line_first, linebuf_char + grid_line_first, n * sizeof(schar_T)); - for (int col = grid_line_first; col < grid_line_last; col++) { + memcpy(scratch_char + first, linebuf_char + first, n * sizeof(schar_T)); + for (int col = first; col < last; col++) { int rev = mirror - col; - if (col + 1 < grid_line_last && scratch_char[col + 1] == 0) { + if (col + 1 < last && scratch_char[col + 1] == 0) { linebuf_char[rev - 1] = scratch_char[col]; linebuf_char[rev] = 0; col++; @@ -495,20 +502,19 @@ void grid_line_mirror(void) // for attr and vcol: assumes doublewidth chars are self-consistent sattr_T *scratch_attr = (sattr_T *)linebuf_scratch; - memcpy(scratch_attr + grid_line_first, linebuf_attr + grid_line_first, n * sizeof(sattr_T)); - for (int col = grid_line_first; col < grid_line_last; col++) { + memcpy(scratch_attr + first, linebuf_attr + first, n * sizeof(sattr_T)); + for (int col = first; col < last; col++) { linebuf_attr[mirror - col] = scratch_attr[col]; } colnr_T *scratch_vcol = (colnr_T *)linebuf_scratch; - memcpy(scratch_vcol + grid_line_first, linebuf_vcol + grid_line_first, n * sizeof(colnr_T)); - for (int col = grid_line_first; col < grid_line_last; col++) { + memcpy(scratch_vcol + first, linebuf_vcol + first, n * sizeof(colnr_T)); + for (int col = first; col < last; col++) { linebuf_vcol[mirror - col] = scratch_vcol[col]; } - int grid_line_last_copy = grid_line_last; - grid_line_last = grid_line_maxcol - grid_line_first; - grid_line_first = grid_line_maxcol - grid_line_last_copy; + *lastp = maxcol - first; + *firstp = maxcol - last; } /// End a group of grid_line_puts calls and send the screen buffer to the UI layer. @@ -698,10 +704,10 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int col, int endcol } } col = endcol + 1; - endcol = (clear_width > 0 ? clear_width : -clear_width); + endcol = clear_width; } - if (p_arshape && !p_tbidi) { + if (p_arshape && !p_tbidi && endcol > col) { line_do_arabic_shape(linebuf_char + col, endcol - col); } diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 2e3cc4f170..61157301b6 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -4135,7 +4135,12 @@ static void qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last, int q // delete all existing lines while ((curbuf->b_ml.ml_flags & ML_EMPTY) == 0) { - (void)ml_delete((linenr_T)1, false); + // If deletion fails, this loop may run forever, so + // signal error and return. + if (ml_delete((linenr_T)1, false) == FAIL) { + internal_error("qf_fill_buffer()"); + return; + } } } diff --git a/src/nvim/spellsuggest.c b/src/nvim/spellsuggest.c index d1f63e537b..938e8cec8f 100644 --- a/src/nvim/spellsuggest.c +++ b/src/nvim/spellsuggest.c @@ -566,7 +566,7 @@ void spell_suggest(int count) } vim_snprintf(IObuff, IOSIZE, "%2d", i + 1); if (cmdmsg_rl) { - rl_mirror_ascii(IObuff); + rl_mirror_ascii(IObuff, NULL); } msg_puts(IObuff); @@ -592,7 +592,7 @@ void spell_suggest(int count) } if (cmdmsg_rl) { // Mirror the numbers, but keep the leading space. - rl_mirror_ascii(IObuff + 1); + rl_mirror_ascii(IObuff + 1, NULL); } msg_advance(30); msg_puts(IObuff); diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index 487f8b010e..4ebf9177de 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -30,6 +30,7 @@ #include "nvim/event/rstream.h" #include "nvim/msgpack_rpc/channel.h" +#define READ_STREAM_SIZE 0xfff #define KEY_BUFFER_SIZE 0xfff static const struct kitty_key_map_entry { @@ -152,7 +153,9 @@ void tinput_init(TermInput *input, Loop *loop) termkey_set_canonflags(input->tk, curflags | TERMKEY_CANON_DELBS); // setup input handle - rstream_init_fd(loop, &input->read_stream, input->in_fd, 0xfff); + rstream_init_fd(loop, &input->read_stream, input->in_fd, READ_STREAM_SIZE); + termkey_set_buffer_size(input->tk, rbuffer_capacity(input->read_stream.buffer)); + // initialize a timer handle for handling ESC with libtermkey time_watcher_init(loop, &input->timer_handle, input); } @@ -479,6 +482,8 @@ static void tk_getkeys(TermInput *input, bool force) } } } + } else if (key.type == TERMKEY_TYPE_OSC) { + handle_osc_event(input, &key); } } @@ -684,6 +689,29 @@ HandleState handle_background_color(TermInput *input) return kComplete; } +static void handle_osc_event(TermInput *input, const TermKeyKey *key) +{ + assert(input); + + const char *str = NULL; + if (termkey_interpret_string(input->tk, key, &str) == TERMKEY_RES_KEY) { + assert(str != NULL); + + // Send an event to nvim core. This will update the v:termresponse variable and fire the + // TermResponse event + MAXSIZE_TEMP_ARRAY(args, 2); + ADD_C(args, STATIC_CSTR_AS_OBJ("osc_response")); + + // libtermkey strips the OSC bytes from the response. We add it back in so that downstream + // consumers of v:termresponse can differentiate between OSC and CSI events. + StringBuilder response = KV_INITIAL_VALUE; + kv_printf(response, "\x1b]%s", str); + ADD_C(args, STRING_OBJ(cbuf_as_string(response.items, response.size))); + rpc_send_event(ui_client_channel_id, "nvim_ui_term_event", args); + kv_destroy(response); + } +} + static void handle_raw_buffer(TermInput *input, bool force) { HandleState is_paste = kNotApplicable; @@ -732,9 +760,9 @@ static void handle_raw_buffer(TermInput *input, bool force) RBUFFER_UNTIL_EMPTY(input->read_stream.buffer, ptr, len) { size_t consumed = termkey_push_bytes(input->tk, ptr, MIN(count, len)); // termkey_push_bytes can return (size_t)-1, so it is possible that - // `consumed > input->read_stream.buffer->size`, but since tk_getkeys is + // `consumed > rbuffer_size(input->read_stream.buffer)`, but since tk_getkeys is // called soon, it shouldn't happen. - assert(consumed <= input->read_stream.buffer->size); + assert(consumed <= rbuffer_size(input->read_stream.buffer)); rbuffer_consumed(input->read_stream.buffer, consumed); // Process the keys now: there is no guarantee `count` will // fit into libtermkey's input buffer. diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua index 613dff577a..befbd4bc3b 100644 --- a/test/functional/core/job_spec.lua +++ b/test/functional/core/job_spec.lua @@ -700,7 +700,7 @@ describe('jobs', function() os.remove('Xtest_jobstart_env') end) - describe('jobwait', function() + describe('jobwait()', function() before_each(function() if is_os('win') then helpers.set_shell_powershell() @@ -874,6 +874,27 @@ describe('jobs', function() eq({'notification', 'wait', {{-1, -1}}}, next_msg()) end) end) + + it('hides cursor when waiting', function() + local screen = Screen.new(30, 3) + screen:set_default_attr_ids({ + [0] = {foreground = Screen.colors.Blue1, bold = true}; + }) + screen:attach() + command([[let g:id = jobstart([v:progpath, '--clean', '--headless'])]]) + feed_command('call jobwait([g:id], 300)') + screen:expect{grid=[[ + | + {0:~ }| + :call jobwait([g:id], 300) | + ]], timeout=100} + funcs.jobstop(meths.get_var('id')) + screen:expect{grid=[[ + ^ | + {0:~ }| + :call jobwait([g:id], 300) | + ]]} + end) end) pending('exit event follows stdout, stderr', function() diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua index c69990d84b..1ebfa9dd1d 100644 --- a/test/functional/lua/vim_spec.lua +++ b/test/functional/lua/vim_spec.lua @@ -2438,6 +2438,12 @@ describe('lua stdlib', function() end) it('allows removing on_key listeners', function() + -- Create some unused namespaces + meths.create_namespace('unused1') + meths.create_namespace('unused2') + meths.create_namespace('unused3') + meths.create_namespace('unused4') + insert([[hello world]]) exec_lua [[ diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index 68c3e73e61..9c16f76359 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -2024,26 +2024,26 @@ describe('extmark decorations', function() it("highlights do reapply to restored text after delete", function() with_undo_restore(true) -- also default behavior - feed 'u' + command('silent undo') screen:expect{grid=[[ ^for _,{1:item in} ipairs(items) do | local text, hl_id_cell, count = unpack(item) | if hl_id_cell ~= nil then | hl_id = hl_id_cell | - 1 change; before #2 0 seconds ago | + | ]]} end) - it("highlights don't reapply to restored text after delete with no_undo_restore", function() + it("highlights don't reapply to restored text after delete with undo_restore=false", function() with_undo_restore(false) - feed 'u' + command('silent undo') screen:expect{grid=[[ ^for _,it{1:em in} ipairs(items) do | local text, hl_id_cell, count = unpack(item) | if hl_id_cell ~= nil then | hl_id = hl_id_cell | - 1 change; before #2 0 seconds ago | + | ]]} eq({ { 1, 0, 8, { end_col = 13, end_right_gravity = false, end_row = 0, |