diff options
author | Justin M. Keyes <justinkz@gmail.com> | 2021-09-16 12:00:13 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-09-16 12:00:13 -0700 |
commit | 7d67bd58655052103f3891122f0e6fe30ae1f829 (patch) | |
tree | 778b8b9ebdf8cec2d90c77eece08abdad94329c8 | |
parent | 942b16adf7312c243fb27e579fa5da794fa0f502 (diff) | |
parent | 2132c063af3adf1d612359e0ed42c7cbd8b4290c (diff) | |
download | rneovim-7d67bd58655052103f3891122f0e6fe30ae1f829.tar.gz rneovim-7d67bd58655052103f3891122f0e6fe30ae1f829.tar.bz2 rneovim-7d67bd58655052103f3891122f0e6fe30ae1f829.zip |
Merge #15677 release-0.5: backports
-rw-r--r-- | runtime/lua/vim/highlight.lua | 6 | ||||
-rw-r--r-- | runtime/lua/vim/lsp.lua | 2 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/diagnostic.lua | 13 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/handlers.lua | 57 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/log.lua | 2 | ||||
-rw-r--r-- | src/nvim/autocmd.c | 4 | ||||
-rw-r--r-- | src/nvim/extmark.c | 2 | ||||
-rw-r--r-- | src/nvim/window.c | 42 | ||||
-rw-r--r-- | test/functional/api/window_spec.lua | 15 | ||||
-rw-r--r-- | test/functional/lua/highlight_spec.lua | 25 | ||||
-rw-r--r-- | test/functional/ui/decorations_spec.lua | 46 | ||||
-rw-r--r-- | test/functional/ui/float_spec.lua | 254 |
12 files changed, 427 insertions, 41 deletions
diff --git a/runtime/lua/vim/highlight.lua b/runtime/lua/vim/highlight.lua index 0012dce081..ef44383f59 100644 --- a/runtime/lua/vim/highlight.lua +++ b/runtime/lua/vim/highlight.lua @@ -85,7 +85,11 @@ function highlight.on_yank(opts) highlight.range(bufnr, yank_ns, higroup, pos1, pos2, event.regtype, event.inclusive) vim.defer_fn( - function() api.nvim_buf_clear_namespace(bufnr, yank_ns, 0, -1) end, + function() + if api.nvim_buf_is_valid(bufnr) then + api.nvim_buf_clear_namespace(bufnr, yank_ns, 0, -1) + end + end, timeout ) end diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 75faf9bcc7..6575e453da 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1150,7 +1150,7 @@ end ---@param bufnr (number) Buffer handle, or 0 for current ---@param client_id (number) the client id function lsp.buf_is_attached(bufnr, client_id) - return (all_buffer_active_clients[bufnr] or {})[client_id] == true + return (all_buffer_active_clients[resolve_bufnr(bufnr)] or {})[client_id] == true end --- Gets a client by id, or nil if the id is invalid. diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua index c67ea0c07a..c83e29aa64 100644 --- a/runtime/lua/vim/lsp/diagnostic.lua +++ b/runtime/lua/vim/lsp/diagnostic.lua @@ -1042,16 +1042,17 @@ function M.on_publish_diagnostics(_, _, params, client_id, _, config) end -- restores the extmarks set by M.display +--- @param last number last line that was changed -- @private -local function restore_extmarks(bufnr) - local lcount = api.nvim_buf_line_count(bufnr) +local function restore_extmarks(bufnr, last) for client_id, extmarks in pairs(diagnostic_cache_extmarks[bufnr]) do local ns = M._get_diagnostic_namespace(client_id) local extmarks_current = api.nvim_buf_get_extmarks(bufnr, ns, 0, -1, {details = true}) local found = {} for _, extmark in ipairs(extmarks_current) do - -- HACK: the missing extmarks seem to still exist, but at the line after the last - if extmark[2] < lcount then + -- nvim_buf_set_lines will move any extmark to the line after the last + -- nvim_buf_set_text will move any extmark to the last line + if extmark[2] ~= last + 1 then found[extmark[1]] = true end end @@ -1076,8 +1077,8 @@ local function save_extmarks(bufnr, client_id) bufnr = bufnr == 0 and api.nvim_get_current_buf() or bufnr if not diagnostic_attached_buffers[bufnr] then api.nvim_buf_attach(bufnr, false, { - on_lines = function() - restore_extmarks(bufnr) + on_lines = function(_, _, _, _, _, last) + restore_extmarks(bufnr, last - 1) end, on_detach = function() diagnostic_cache_extmarks[bufnr] = nil diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 41852b9d88..0c0aa0ceb6 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -18,10 +18,8 @@ local function err_message(...) end --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_executeCommand -M['workspace/executeCommand'] = function(err, _) - if err then - error("Could not execute code action: "..err.message) - end +M['workspace/executeCommand'] = function() + -- Error handling is done implicitly by wrapping all handlers; see end of this file end -- @msg of type ProgressParams @@ -158,13 +156,12 @@ M['workspace/applyEdit'] = function(_, _, workspace_edit) end --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_configuration -M['workspace/configuration'] = function(err, _, params, client_id) +M['workspace/configuration'] = function(_, _, params, client_id) local client = vim.lsp.get_client_by_id(client_id) if not client then err_message("LSP[id=", client_id, "] client has shut down after sending the message") return end - if err then error(vim.inspect(err)) end if not params.items then return {} end @@ -191,30 +188,33 @@ M['textDocument/codeLens'] = function(...) return require('vim.lsp.codelens').on_codelens(...) end ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references -M['textDocument/references'] = function(_, _, result) - if not result then return end - util.set_qflist(util.locations_to_items(result)) - api.nvim_command("copen") -end ---@private ---- Prints given list of symbols to the quickfix list. ---@param _ (not used) ---@param _ (not used) ---@param result (list of Symbols) LSP method name ---@param result (table) result of LSP method; a location or a list of locations. ----(`textDocument/definition` can return `Location` or `Location[]` -local symbol_handler = function(_, _, result, _, bufnr) - if not result or vim.tbl_isempty(result) then return end - util.set_qflist(util.symbols_to_items(result, bufnr)) - api.nvim_command("copen") +--@private +--- Return a function that converts LSP responses to quickfix items and opens the qflist +-- +--@param map_result function `((resp, bufnr) -> list)` to convert the response +--@param entity name of the resource used in a `not found` error message +local function response_to_qflist(map_result, entity) + return function(_, _, result, _, bufnr) + if not result or vim.tbl_isempty(result) then + vim.notify('No ' .. entity .. ' found') + else + util.set_qflist(map_result(result, bufnr)) + api.nvim_command("copen") + end + end end + + +--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references +M['textDocument/references'] = response_to_qflist(util.locations_to_items, 'references') + --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentSymbol -M['textDocument/documentSymbol'] = symbol_handler +M['textDocument/documentSymbol'] = response_to_qflist(util.symbols_to_items, 'document symbols') + --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_symbol -M['workspace/symbol'] = symbol_handler +M['workspace/symbol'] = response_to_qflist(util.symbols_to_items, 'symbols') --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_rename M['textDocument/rename'] = function(_, _, result) @@ -436,7 +436,12 @@ for k, fn in pairs(M) do }) if err then - return err_message(tostring(err)) + -- LSP spec: + -- interface ResponseError: + -- code: integer; + -- message: string; + -- data?: string | number | boolean | array | object | null; + return err_message(tostring(err.code) .. ': ' .. err.message) end return fn(err, method, params, client_id, bufnr, config) diff --git a/runtime/lua/vim/lsp/log.lua b/runtime/lua/vim/lsp/log.lua index 471a311c16..73fafb9715 100644 --- a/runtime/lua/vim/lsp/log.lua +++ b/runtime/lua/vim/lsp/log.lua @@ -17,7 +17,7 @@ local current_log_level = log.levels.WARN local log_date_format = "%FT%H:%M:%S%z" do - local path_sep = vim.loop.os_uname().sysname == "Windows" and "\\" or "/" + local path_sep = vim.loop.os_uname().version:match("Windows") and "\\" or "/" --@private local function path_join(...) return table.concat(vim.tbl_flatten{...}, path_sep) diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 145f6f5601..bec9808183 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -1724,7 +1724,7 @@ BYPASS_AU: void block_autocmds(void) { // Remember the value of v:termresponse. - if (is_autocmd_blocked()) { + if (!is_autocmd_blocked()) { old_termresponse = get_vim_var_str(VV_TERMRESPONSE); } autocmd_blocked++; @@ -1737,7 +1737,7 @@ void unblock_autocmds(void) // When v:termresponse was set while autocommands were blocked, trigger // the autocommands now. Esp. useful when executing a shell command // during startup (nvim -d). - if (is_autocmd_blocked() + if (!is_autocmd_blocked() && get_vim_var_str(VV_TERMRESPONSE) != old_termresponse) { apply_autocmds(EVENT_TERMRESPONSE, NULL, NULL, false, curbuf); } diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c index 2906a2196b..62ccf89484 100644 --- a/src/nvim/extmark.c +++ b/src/nvim/extmark.c @@ -268,7 +268,7 @@ bool extmark_clear(buf_T *buf, uint64_t ns_id, } ExtmarkNs *my_ns = all_ns ? buf_ns_ref(buf, item.ns_id, false) : ns; map_del(uint64_t, uint64_t)(my_ns->map, item.mark_id); - map_del(uint64_t, ExtmarkItem)(buf->b_extmark_index, mark.id); + map_del(uint64_t, ExtmarkItem)(buf->b_extmark_index, start_id); marktree_del_itr(buf->b_marktree, itr, false); } else { marktree_itr_next(buf->b_marktree, itr); diff --git a/src/nvim/window.c b/src/nvim/window.c index d051e8e467..a64dee24a0 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -676,7 +676,7 @@ void win_set_minimal_style(win_T *wp) } // signcolumn: use 'auto' - if (wp->w_p_scl[0] != 'a') { + if (wp->w_p_scl[0] != 'a' || STRLEN(wp->w_p_scl) >= 8) { xfree(wp->w_p_scl); wp->w_p_scl = (char_u *)xstrdup("auto"); } @@ -737,6 +737,37 @@ void win_config_float(win_T *wp, FloatConfig fconfig) redraw_later(wp, NOT_VALID); } + // compute initial position + if (wp->w_float_config.relative == kFloatRelativeWindow) { + int row = wp->w_float_config.row; + int col = wp->w_float_config.col; + Error dummy = ERROR_INIT; + win_T *parent = find_window_by_handle(wp->w_float_config.window, &dummy); + if (parent) { + row += parent->w_winrow; + col += parent->w_wincol; + ScreenGrid *grid = &parent->w_grid; + int row_off = 0, col_off = 0; + screen_adjust_grid(&grid, &row_off, &col_off); + row += row_off; + col += col_off; + } + api_clear_error(&dummy); + if (wp->w_float_config.bufpos.lnum >= 0) { + pos_T pos = { wp->w_float_config.bufpos.lnum + 1, + wp->w_float_config.bufpos.col, 0 }; + int trow, tcol, tcolc, tcole; + textpos2screenpos(wp, &pos, &trow, &tcol, &tcolc, &tcole, true); + row += trow - 1; + col += tcol - 1; + } + wp->w_winrow = row; + wp->w_wincol = col; + } else { + wp->w_winrow = fconfig.row; + wp->w_wincol = fconfig.col; + } + // changing border style while keeping border only requires redrawing border if (fconfig.border) { wp->w_redr_border = true; @@ -770,7 +801,6 @@ int win_fdccol_count(win_T *wp) } } - void ui_ext_win_position(win_T *wp) { if (!wp->w_floating) { @@ -817,6 +847,8 @@ void ui_ext_win_position(win_T *wp) int comp_row = (int)row - (south ? wp->w_height : 0); int comp_col = (int)col - (east ? wp->w_width : 0); + comp_row += grid->comp_row; + comp_col += grid->comp_col; comp_row = MAX(MIN(comp_row, Rows-wp->w_height_outer-1), 0); comp_col = MAX(MIN(comp_col, Columns-wp->w_width_outer), 0); wp->w_winrow = comp_row; @@ -2702,7 +2734,11 @@ static win_T *win_free_mem( // When deleting the current window of another tab page select a new // current window. if (tp != NULL && win == tp->tp_curwin) { - tp->tp_curwin = wp; + if (win_valid(tp->tp_prevwin) && tp->tp_prevwin != win) { + tp->tp_curwin = tp->tp_prevwin; + } else { + tp->tp_curwin = tp->tp_firstwin; + } } return wp; diff --git a/test/functional/api/window_spec.lua b/test/functional/api/window_spec.lua index bb72b63b6c..8c79e6a016 100644 --- a/test/functional/api/window_spec.lua +++ b/test/functional/api/window_spec.lua @@ -346,6 +346,21 @@ describe('API/win', function() eq(2, #meths.list_wins()) eq('', funcs.getcmdwintype()) end) + + it('closing current (float) window of another tabpage #15313', function() + command('tabedit') + eq(2, eval('tabpagenr()')) + local win = meths.open_win(0, true, { + relative='editor', row=10, col=10, width=50, height=10 + }) + local tabpage = eval('tabpagenr()') + command('tabprevious') + eq(1, eval('tabpagenr()')) + meths.win_close(win, false) + + eq(1001, meths.tabpage_get_win(tabpage).id) + helpers.assert_alive() + end) end) describe('hide', function() diff --git a/test/functional/lua/highlight_spec.lua b/test/functional/lua/highlight_spec.lua new file mode 100644 index 0000000000..50eecb5d09 --- /dev/null +++ b/test/functional/lua/highlight_spec.lua @@ -0,0 +1,25 @@ +local helpers = require('test.functional.helpers')(after_each) +local exec_lua = helpers.exec_lua +local eq = helpers.eq +local eval = helpers.eval +local command = helpers.command +local clear = helpers.clear + +describe('vim.highlight.on_yank', function() + + before_each(function() + clear() + end) + + it('does not show errors even if buffer is wiped before timeout', function() + command('new') + exec_lua[[ + vim.highlight.on_yank({timeout = 10, on_macro = true, event = {operator = "y", regtype = "v"}}) + vim.cmd('bwipeout!') + ]] + helpers.sleep(10) + helpers.feed('<cr>') -- avoid hang if error message exists + eq('', eval('v:errmsg')) + end) + +end) diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index 98aafd8757..0e4b3574e9 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -697,4 +697,50 @@ end]] | ]]} end) + it('does not crash when deleting a cleared buffer #15212', function() + exec_lua [[ + ns = vim.api.nvim_create_namespace("myplugin") + vim.api.nvim_buf_set_extmark(0, ns, 0, 0, {virt_text = {{"a"}}, end_col = 0}) + ]] + screen:expect{grid=[[ + ^ a | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + exec_lua [[ + vim.api.nvim_buf_clear_namespace(0, ns, 0, -1) + vim.cmd("bdelete") + ]] + screen:expect{grid=[[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + helpers.assert_alive() + end) end) diff --git a/test/functional/ui/float_spec.lua b/test/functional/ui/float_spec.lua index 61ed0a65b0..1457c62b54 100644 --- a/test/functional/ui/float_spec.lua +++ b/test/functional/ui/float_spec.lua @@ -152,6 +152,132 @@ describe('float window', function() eq(10, width) end) + it('opened with correct position', function() + local pos = exec_lua([[ + local bufnr = vim.api.nvim_create_buf(false, true) + + local opts = { + width = 10, + height = 10, + col = 7, + row = 9, + relative = 'editor', + style = 'minimal' + } + + local win_id = vim.api.nvim_open_win(bufnr, false, opts) + + return vim.api.nvim_win_get_position(win_id) + ]]) + + eq(9, pos[1]) + eq(7, pos[2]) + end) + + it('opened with correct position relative to the cursor', function() + local pos = exec_lua([[ + local bufnr = vim.api.nvim_create_buf(false, true) + + local opts = { + width = 10, + height = 10, + col = 7, + row = 9, + relative = 'cursor', + style = 'minimal' + } + + local win_id = vim.api.nvim_open_win(bufnr, false, opts) + + return vim.api.nvim_win_get_position(win_id) + ]]) + + eq(9, pos[1]) + eq(7, pos[2]) + end) + + it('opened with correct position relative to another window', function() + local pos = exec_lua([[ + local bufnr = vim.api.nvim_create_buf(false, true) + + local par_opts = { + width = 50, + height = 50, + col = 7, + row = 9, + relative = 'editor', + style = 'minimal' + } + + local par_win_id = vim.api.nvim_open_win(bufnr, false, par_opts) + + local opts = { + width = 10, + height = 10, + col = 7, + row = 9, + relative = 'win', + style = 'minimal', + win = par_win_id + } + + local win_id = vim.api.nvim_open_win(bufnr, false, opts) + + return vim.api.nvim_win_get_position(win_id) + ]]) + + eq(18, pos[1]) + eq(14, pos[2]) + end) + + + it('opened with correct position relative to another relative window', function() + local pos = exec_lua([[ + local bufnr = vim.api.nvim_create_buf(false, true) + + local root_opts = { + width = 50, + height = 50, + col = 7, + row = 9, + relative = 'editor', + style = 'minimal' + } + + local root_win_id = vim.api.nvim_open_win(bufnr, false, root_opts) + + local par_opts = { + width = 20, + height = 20, + col = 2, + row = 3, + relative = 'win', + win = root_win_id, + style = 'minimal' + } + + local par_win_id = vim.api.nvim_open_win(bufnr, false, par_opts) + + local opts = { + width = 10, + height = 10, + col = 3, + row = 2, + relative = 'win', + win = par_win_id, + style = 'minimal' + } + + local win_id = vim.api.nvim_open_win(bufnr, false, opts) + + return vim.api.nvim_win_get_position(win_id) + ]]) + + eq(14, pos[1]) + eq(12, pos[2]) + end) + + local function with_ext_multigrid(multigrid) local screen before_each(function() @@ -620,6 +746,134 @@ describe('float window', function() end end) + it("would not break 'minimal' style with signcolumn=auto:[min]-[max]", function() + command('set number') + command('set signcolumn=auto:1-3') + command('set colorcolumn=1') + command('set cursorline') + command('set foldcolumn=1') + command('hi NormalFloat guibg=#333333') + feed('ix<cr>y<cr><esc>gg') + local win = meths.open_win(0, false, {relative='editor', width=20, height=4, row=4, col=10, style='minimal'}) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + {19: }{20: 1 }{22:^x}{21: }| + {19: }{14: 2 }{22:y} | + {19: }{14: 3 }{22: } | + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 4 + {15:x }| + {15:y }| + {15: }| + {15: }| + ]], float_pos={[4] = {{id = 1001}, "NW", 1, 4, 10, true}}} + else + screen:expect{grid=[[ + {19: }{20: 1 }{22:^x}{21: }| + {19: }{14: 2 }{22:y} | + {19: }{14: 3 }{22: } {15:x } | + {0:~ }{15:y }{0: }| + {0:~ }{15: }{0: }| + {0:~ }{15: }{0: }| + | + ]]} + end + + command('sign define piet1 text=𐌢̀́̂̃̅̄𐌢̀́̂̃̅̄ texthl=Search') + command('sign place 1 line=1 name=piet1 buffer=1') + -- signcolumn=auto:1-3 still works if there actually are signs + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + {19: }{17:𐌢̀́̂̃̅̄𐌢̀́̂̃̅̄}{20: 1 }{22:^x}{21: }| + {19: }{14: 2 }{22:y} | + {19: }{14: 3 }{22: } | + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 4 + {17:𐌢̀́̂̃̅̄𐌢̀́̂̃̅̄}{15:x }| + {19: }{15:y }| + {19: }{15: }| + {15: }| + ]], float_pos={[4] = {{id = 1001}, "NW", 1, 4, 10, true}}} + + else + screen:expect([[ + {19: }{17:𐌢̀́̂̃̅̄𐌢̀́̂̃̅̄}{20: 1 }{22:^x}{21: }| + {19: }{14: 2 }{22:y} | + {19: }{14: 3 }{22: } {17:𐌢̀́̂̃̅̄𐌢̀́̂̃̅̄}{15:x } | + {0:~ }{19: }{15:y }{0: }| + {0:~ }{19: }{15: }{0: }| + {0:~ }{15: }{0: }| + | + ]]) + end + command('sign unplace 1 buffer=1') + + local buf = meths.create_buf(false, true) + meths.win_set_buf(win, buf) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + {19: }{20: 1 }{22:^x}{21: }| + {19: }{14: 2 }{22:y} | + {19: }{14: 3 }{22: } | + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 4 + {15: }| + {15: }| + {15: }| + {15: }| + ]], float_pos={[4] = {{id = 1001}, "NW", 1, 4, 10, true}}} + else + screen:expect([[ + {19: }{20: 1 }{22:^x}{21: }| + {19: }{14: 2 }{22:y} | + {19: }{14: 3 }{22: } {15: } | + {0:~ }{15: }{0: }| + {0:~ }{15: }{0: }| + {0:~ }{15: }{0: }| + | + ]]) + end + end) + it('can have border', function() local buf = meths.create_buf(false, false) meths.buf_set_lines(buf, 0, -1, true, {' halloj! ', |