diff options
-rw-r--r-- | config/CMakeLists.txt | 2 | ||||
-rw-r--r-- | runtime/doc/autocmd.txt | 1 | ||||
-rw-r--r-- | runtime/doc/eval.txt | 2 | ||||
-rw-r--r-- | runtime/doc/lsp.txt | 8 | ||||
-rw-r--r-- | runtime/doc/lua.txt | 14 | ||||
-rw-r--r-- | runtime/lua/vim/highlight.lua | 29 | ||||
-rw-r--r-- | runtime/lua/vim/lsp.lua | 2 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/callbacks.lua | 11 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/protocol.lua | 22 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/util.lua | 213 | ||||
-rw-r--r-- | runtime/lua/vim/shared.lua | 2 | ||||
-rwxr-xr-x | scripts/vim-patch.sh | 2 | ||||
-rw-r--r-- | src/nvim/ops.c | 4 | ||||
-rw-r--r-- | src/nvim/testdir/test_autocmd.vim | 10 | ||||
-rw-r--r-- | test/functional/autocmd/textyankpost_spec.lua | 48 | ||||
-rw-r--r-- | test/functional/lua/vim_spec.lua | 2 | ||||
-rw-r--r-- | test/functional/plugin/lsp_spec.lua | 74 |
17 files changed, 336 insertions, 110 deletions
diff --git a/config/CMakeLists.txt b/config/CMakeLists.txt index 0ca41d5dfd..6c9e06d59d 100644 --- a/config/CMakeLists.txt +++ b/config/CMakeLists.txt @@ -12,7 +12,7 @@ check_type_size("size_t" SIZEOF_SIZE_T) check_type_size("long long" SIZEOF_LONG_LONG) check_type_size("void *" SIZEOF_VOID_PTR) -if (CMAKE_HOST_SYSTEM_VERSION MATCHES ".*-Microsoft") +if (CMAKE_HOST_SYSTEM_VERSION MATCHES ".*-(Microsoft|microsoft-standard)") # Windows Subsystem for Linux set(HAVE_WSL 1) endif() diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index 64ca7b6a45..f1753b75cc 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -844,6 +844,7 @@ TextYankPost Just after a |yank| or |deleting| command, but not regcontents regname regtype + visual The `inclusive` flag combined with the |'[| and |']| marks can be used to calculate the precise region of the operation. diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 214d815006..7f50769023 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1591,6 +1591,8 @@ v:event Dictionary of event data for the current |autocommand|. Valid operation. regtype Type of register as returned by |getregtype()|. + visual Selection is visual (as opposed to, + e.g., via motion). completed_item Current selected complete item on |CompleteChanged|, Is `{}` when no complete item selected. diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index 0c510c3bec..9deaf26983 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -1042,14 +1042,6 @@ get_current_line_to_cursor() get_severity_highlight_name({severity}) TODO: Documentation - *vim.lsp.util.highlight_range()* -highlight_range({bufnr}, {ns}, {hiname}, {start}, {finish}) - TODO: Documentation - - *vim.lsp.util.highlight_region()* -highlight_region({ft}, {start}, {finish}) - TODO: Documentation - jump_to_location({location}) *vim.lsp.util.jump_to_location()* TODO: Documentation diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index 19579a2a3c..5a49d36503 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -705,6 +705,10 @@ the highlight via > au TextYankPost * silent! lua require'vim.highlight'.on_yank("IncSearch", 500) < +If you want to exclude visual selections from highlighting on yank, use +> +au TextYankPost * silent! lua return (not vim.v.event.visual) and require'vim.highlight'.on_yank() +< vim.highlight.on_yank([{higroup}, {timeout}, {event}]) *vim.highlight.on_yank()* @@ -713,6 +717,16 @@ vim.highlight.on_yank([{higroup}, {timeout}, {event}]) in milliseconds ({timeout}, default `500`), and the event structure that is fired ({event}, default `vim.v.event`). + +vim.highlight.range({bufnr}, {ns}, {higroup}, {start}, {finish}, {rtype}, {inclusive}) + *vim.highlight.range()* + Highlights the range between {start} and {finish} (tuples of {line,col}) + in buffer {bufnr} with the highlight group {higroup} using the namespace + {ns}. Optional arguments are the type of range (characterwise, linewise, + or blockwise, see |setreg|; default to characterwise) and whether the + range is inclusive (default false). + + ------------------------------------------------------------------------------ VIM.REGEX *lua-regex* diff --git a/runtime/lua/vim/highlight.lua b/runtime/lua/vim/highlight.lua index 5c98c626a4..69c3c8a4dc 100644 --- a/runtime/lua/vim/highlight.lua +++ b/runtime/lua/vim/highlight.lua @@ -2,12 +2,34 @@ local api = vim.api local highlight = {} +--- Highlight range between two positions +--- +--@param bufnr number of buffer to apply highlighting to +--@param ns namespace to add highlight to +--@param higroup highlight group to use for highlighting +--@param rtype type of range (:help setreg, default charwise) +--@param inclusive boolean indicating whether the range is end-inclusive (default false) +function highlight.range(bufnr, ns, higroup, start, finish, rtype, inclusive) + rtype = rtype or 'v' + inclusive = inclusive or false + + -- sanity check + if start[2] < 0 or finish[2] < start[2] then return end + + local region = vim.region(bufnr, start, finish, rtype, inclusive) + for linenr, cols in pairs(region) do + api.nvim_buf_add_highlight(bufnr, ns, higroup, linenr, cols[1], cols[2]) + end + +end + --- Highlight the yanked region --- +--- --- use from init.vim via --- au TextYankPost * lua require'vim.highlight'.on_yank() --- customize highlight group and timeout via --- au TextYankPost * lua require'vim.highlight'.on_yank("IncSearch", 500) +--- -- @param higroup highlight group for yanked region -- @param timeout time in ms before highlight is cleared -- @param event event structure @@ -27,10 +49,7 @@ function highlight.on_yank(higroup, timeout, event) pos1 = {pos1[2] - 1, pos1[3] - 1 + pos1[4]} pos2 = {pos2[2] - 1, pos2[3] - 1 + pos2[4]} - local region = vim.region(bufnr, pos1, pos2, event.regtype, event.inclusive) - for linenr, cols in pairs(region) do - api.nvim_buf_add_highlight(bufnr, yank_ns, higroup, linenr, cols[1], cols[2]) - end + 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, diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 84812b8c64..2fbc51481f 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -507,6 +507,8 @@ function lsp.start_client(config) or (not client.resolved_capabilities.signature_help and method == 'textDocument/signatureHelp') or (not client.resolved_capabilities.goto_definition and method == 'textDocument/definition') or (not client.resolved_capabilities.implementation and method == 'textDocument/implementation') + or (not client.resolved_capabilities.declaration and method == 'textDocument/declaration') + or (not client.resolved_capabilities.type_definition and method == 'textDocument/typeDefinition') or (not client.resolved_capabilities.document_symbol and method == 'textDocument/documentSymbol') or (not client.resolved_capabilities.workspace_symbol and method == 'textDocument/workspaceSymbol') then diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua index 7c51fc2cc2..4b14f0132d 100644 --- a/runtime/lua/vim/lsp/callbacks.lua +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -72,6 +72,17 @@ M['textDocument/publishDiagnostics'] = function(_, _, result) err_message("LSP.publishDiagnostics: Couldn't find buffer for ", uri) return end + + -- Unloaded buffers should not handle diagnostics. + -- When the buffer is loaded, we'll call on_attach, which sends textDocument/didOpen. + -- This should trigger another publish of the diagnostics. + -- + -- In particular, this stops a ton of spam when first starting a server for current + -- unloaded buffers. + if not api.nvim_buf_is_loaded(bufnr) then + return + end + util.buf_clear_diagnostics(bufnr) -- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#diagnostic diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index 7d5f8f5ef1..64911fe7bb 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -923,6 +923,28 @@ function protocol.resolve_capabilities(server_capabilities) error("The server sent invalid codeActionProvider") end + if server_capabilities.declarationProvider == nil then + general_properties.declaration = false + elseif type(server_capabilities.declarationProvider) == 'boolean' then + general_properties.declaration = server_capabilities.declarationProvider + elseif type(server_capabilities.declarationProvider) == 'table' then + -- TODO: support more detailed declarationProvider options. + general_properties.declaration = false + else + error("The server sent invalid declarationProvider") + end + + if server_capabilities.typeDefinitionProvider == nil then + general_properties.type_definition = false + elseif type(server_capabilities.typeDefinitionProvider) == 'boolean' then + general_properties.type_definition = server_capabilities.typeDefinitionProvider + elseif type(server_capabilities.typeDefinitionProvider) == 'table' then + -- TODO: support more detailed typeDefinitionProvider options. + general_properties.type_definition = false + else + error("The server sent invalid typeDefinitionProvider") + end + if server_capabilities.implementationProvider == nil then general_properties.implementation = false elseif type(server_capabilities.implementationProvider) == 'boolean' then diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 752d4ff439..49e2557c16 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -3,6 +3,7 @@ local vim = vim local validate = vim.validate local api = vim.api local list_extend = vim.list_extend +local highlight = require 'vim.highlight' local M = {} @@ -94,17 +95,23 @@ local edit_sort_key = sort_by_key(function(e) return {e.A[1], e.A[2], e.i} end) -local function get_line_byte_from_line_character(bufnr, lnum, cnum) - -- Skip check when the byte and character position is the same - if cnum > 0 then - local lines = api.nvim_buf_get_lines(bufnr, lnum, lnum+1, false) - +--- Position is a https://microsoft.github.io/language-server-protocol/specifications/specification-current/#position +-- Returns a zero-indexed column, since set_lines() does the conversion to +-- 1-indexed +local function get_line_byte_from_position(bufnr, position) + -- LSP's line and characters are 0-indexed + -- Vim's line and columns are 1-indexed + local col = position.character + -- When on the first character, we can ignore the difference between byte and + -- character + if col > 0 then + local line = position.line + local lines = api.nvim_buf_get_lines(bufnr, line, line + 1, false) if #lines > 0 then - return vim.str_byteindex(lines[1], cnum) + return vim.str_byteindex(lines[1], col) end end - - return cnum + return col end function M.apply_text_edits(text_edits, bufnr) @@ -117,15 +124,9 @@ function M.apply_text_edits(text_edits, bufnr) for i, e in ipairs(text_edits) do -- adjust start and end column for UTF-16 encoding of non-ASCII characters local start_row = e.range.start.line - local start_col = get_line_byte_from_line_character( - bufnr, - start_row, - e.range.start.character) + local start_col = get_line_byte_from_position(bufnr, e.range.start) local end_row = e.range["end"].line - local end_col = get_line_byte_from_line_character( - bufnr, - end_row, - e.range["end"].character) + local end_col = get_line_byte_from_position(bufnr, e.range['end']) start_line = math.min(e.range.start.line, start_line) finish_line = math.max(e.range["end"].line, finish_line) -- TODO(ashkan) sanity check ranges for overlap. @@ -288,7 +289,7 @@ local function get_completion_word(item) return item.label end --- Some lanuguage servers return complementary candidates whose prefixes do not match are also returned. +-- Some language servers return complementary candidates whose prefixes do not match are also returned. -- So we exclude completion candidates whose prefix does not match. local function remove_unmatch_completion_items(items, prefix) return vim.tbl_filter(function(item) @@ -542,9 +543,7 @@ function M.jump_to_location(location) api.nvim_buf_set_option(0, 'buflisted', true) local range = location.range or location.targetSelectionRange local row = range.start.line - local col = range.start.character - local line = api.nvim_buf_get_lines(0, row, row+1, true)[1] - col = vim.str_byteindex(line, col) + local col = get_line_byte_from_position(0, range.start) api.nvim_win_set_cursor(0, {row + 1, col}) return true end @@ -615,13 +614,53 @@ function M.focusable_preview(unique_name, fn) end) end --- Convert markdown into syntax highlighted regions by stripping the code --- blocks and converting them into highlighted code. --- This will by default insert a blank line separator after those code block --- regions to improve readability. +--- Trim empty lines from input and pad left and right with spaces +--- +--@param contents table of lines to trim and pad +--@param opts dictionary with optional fields +-- - pad_left amount of columns to pad contents at left (default 1) +-- - pad_right amount of columns to pad contents at right (default 1) +--@return contents table of trimmed and padded lines +function M._trim_and_pad(contents, opts) + validate { + contents = { contents, 't' }; + opts = { opts, 't', true }; + } + opts = opts or {} + local left_padding = (" "):rep(opts.pad_left or 1) + local right_padding = (" "):rep(opts.pad_right or 1) + contents = M.trim_empty_lines(contents) + for i, line in ipairs(contents) do + contents[i] = string.format('%s%s%s', left_padding, line:gsub("\r", ""), right_padding) + end + return contents +end + + + +--- Convert markdown into syntax highlighted regions by stripping the code +--- blocks and converting them into highlighted code. +--- This will by default insert a blank line separator after those code block +--- regions to improve readability. +--- The result is shown in a floating preview +--- TODO: refactor to separate stripping/converting and make use of open_floating_preview +--- +--@param contents table of lines to show in window +--@param opts dictionary with optional fields +-- - height of floating window +-- - width of floating window +-- - wrap_at character to wrap at for computing height +-- - pad_left amount of columns to pad contents at left +-- - pad_right amount of columns to pad contents at right +-- - separator insert separator after code block +--@return width,height size of float function M.fancy_floating_markdown(contents, opts) - local pad_left = opts and opts.pad_left - local pad_right = opts and opts.pad_right + validate { + contents = { contents, 't' }; + opts = { opts, 't', true }; + } + opts = opts or {} + local stripped = {} local highlights = {} do @@ -655,31 +694,27 @@ function M.fancy_floating_markdown(contents, opts) end end end - local width = 0 - for i, v in ipairs(stripped) do - v = v:gsub("\r", "") - if pad_left then v = (" "):rep(pad_left)..v end - if pad_right then v = v..(" "):rep(pad_right) end - stripped[i] = v - width = math.max(width, #v) - end - if opts and opts.max_width then - width = math.min(opts.max_width, width) - end - -- TODO(ashkan): decide how to make this customizable. - local insert_separator = true + -- Clean up and add padding + stripped = M._trim_and_pad(stripped, opts) + + -- Compute size of float needed to show (wrapped) lines + opts.wrap_at = opts.wrap_at or (vim.wo["wrap"] and api.nvim_win_get_width(0)) + local width, height = M._make_floating_popup_size(stripped, opts) + + -- Insert blank line separator after code block + local insert_separator = opts.separator or true if insert_separator then for i, h in ipairs(highlights) do h.start = h.start + i - 1 h.finish = h.finish + i - 1 if h.finish + 1 <= #stripped then table.insert(stripped, h.finish + 1, string.rep("─", width)) + height = height + 1 end end end -- Make the floating window. - local height = #stripped local bufnr = api.nvim_create_buf(false, true) local winnr = api.nvim_open_win(bufnr, false, M.make_floating_popup_options(width, height, opts)) vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, stripped) @@ -691,7 +726,7 @@ function M.fancy_floating_markdown(contents, opts) vim.cmd("ownsyntax markdown") local idx = 1 - local function highlight_region(ft, start, finish) + local function apply_syntax_to_region(ft, start, finish) if ft == '' then return end local name = ft..idx idx = idx + 1 @@ -707,8 +742,8 @@ function M.fancy_floating_markdown(contents, opts) -- make sure that regions between code blocks are definitely markdown. -- local ph = {start = 0; finish = 1;} for _, h in ipairs(highlights) do - -- highlight_region('markdown', ph.finish, h.start) - highlight_region(h.ft, h.start, h.finish) + -- apply_syntax_to_region('markdown', ph.finish, h.start) + apply_syntax_to_region(h.ft, h.start, h.finish) -- ph = h end @@ -720,33 +755,81 @@ function M.close_preview_autocmd(events, winnr) api.nvim_command("autocmd "..table.concat(events, ',').." <buffer> ++once lua pcall(vim.api.nvim_win_close, "..winnr..", true)") end -function M.open_floating_preview(contents, filetype, opts) +--- Compute size of float needed to show contents (with optional wrapping) +--- +--@param contents table of lines to show in window +--@param opts dictionary with optional fields +-- - height of floating window +-- - width of floating window +-- - wrap_at character to wrap at for computing height +--@return width,height size of float +function M._make_floating_popup_size(contents, opts) validate { contents = { contents, 't' }; - filetype = { filetype, 's', true }; opts = { opts, 't', true }; } opts = opts or {} - -- Trim empty lines from the end. - contents = M.trim_empty_lines(contents) - local width = opts.width - local height = opts.height or #contents + local height = opts.height + local line_widths = {} + if not width then width = 0 for i, line in ipairs(contents) do - -- Clean up the input and add left pad. - line = " "..line:gsub("\r", "") -- TODO(ashkan) use nvim_strdisplaywidth if/when that is introduced. - local line_width = vim.fn.strdisplaywidth(line) - width = math.max(line_width, width) - contents[i] = line + line_widths[i] = vim.fn.strdisplaywidth(line) + width = math.max(line_widths[i], width) end - -- Add right padding of 1 each. - width = width + 1 end + if not height then + height = #contents + local wrap_at = opts.wrap_at + if wrap_at and width > wrap_at then + height = 0 + if vim.tbl_isempty(line_widths) then + for _, line in ipairs(contents) do + local line_width = vim.fn.strdisplaywidth(line) + height = height + math.ceil(line_width/wrap_at) + end + else + for i = 1, #contents do + height = height + math.ceil(line_widths[i]/wrap_at) + end + end + end + end + + return width, height +end + +--- Show contents in a floating window +--- +--@param contents table of lines to show in window +--@param filetype string of filetype to set for opened buffer +--@param opts dictionary with optional fields +-- - height of floating window +-- - width of floating window +-- - wrap_at character to wrap at for computing height +-- - pad_left amount of columns to pad contents at left +-- - pad_right amount of columns to pad contents at right +--@return bufnr,winnr buffer and window number of floating window or nil +function M.open_floating_preview(contents, filetype, opts) + validate { + contents = { contents, 't' }; + filetype = { filetype, 's', true }; + opts = { opts, 't', true }; + } + opts = opts or {} + + -- Clean up input: trim empty lines from the end, pad + contents = M._trim_and_pad(contents, opts) + + -- Compute size of float needed to show (wrapped) lines + opts.wrap_at = opts.wrap_at or (vim.wo["wrap"] and api.nvim_win_get_width(0)) + local width, height = M._make_floating_popup_size(contents, opts) + local floating_bufnr = api.nvim_create_buf(false, true) if filetype then api.nvim_buf_set_option(floating_bufnr, 'filetype', filetype) @@ -762,19 +845,6 @@ function M.open_floating_preview(contents, filetype, opts) return floating_bufnr, floating_winnr end -local function highlight_range(bufnr, ns, hiname, start, finish) - if start[1] == finish[1] then - -- TODO care about encoding here since this is in byte index? - api.nvim_buf_add_highlight(bufnr, ns, hiname, start[1], start[2], finish[2]) - else - api.nvim_buf_add_highlight(bufnr, ns, hiname, start[1], start[2], -1) - for line = start[1] + 1, finish[1] - 1 do - api.nvim_buf_add_highlight(bufnr, ns, hiname, line, 0, -1) - end - api.nvim_buf_add_highlight(bufnr, ns, hiname, finish[1], 0, finish[2]) - end -end - do local diagnostic_ns = api.nvim_create_namespace("vim_lsp_diagnostics") local reference_ns = api.nvim_create_namespace("vim_lsp_references") @@ -908,8 +978,7 @@ do [protocol.DiagnosticSeverity.Hint]='Hint', } - -- TODO care about encoding here since this is in byte index? - highlight_range(bufnr, diagnostic_ns, + highlight.range(bufnr, diagnostic_ns, underline_highlight_name..hlmap[diagnostic.severity], {start.line, start.character}, {finish.line, finish.character} @@ -933,7 +1002,7 @@ do [protocol.DocumentHighlightKind.Write] = "LspReferenceWrite"; } local kind = reference["kind"] or protocol.DocumentHighlightKind.Text - highlight_range(bufnr, reference_ns, document_highlight_kind[kind], start_pos, end_pos) + highlight.range(bufnr, reference_ns, document_highlight_kind[kind], start_pos, end_pos) end end diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index 2135bfc837..384d22cb89 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -79,7 +79,7 @@ function vim.gsplit(s, sep, plain) end return function() - if done then + if done or (s == '' and sep == '') then return end if sep == '' then diff --git a/scripts/vim-patch.sh b/scripts/vim-patch.sh index a8b622d5c4..9c4349abca 100755 --- a/scripts/vim-patch.sh +++ b/scripts/vim-patch.sh @@ -7,7 +7,7 @@ set -p # Ensure that the user has a bash that supports -A if [[ "${BASH_VERSINFO[0]}" -lt 4 ]]; then - echo "This script requires bash version 3 or later (you have ${BASH_VERSION})." >&2 + >&2 echo "error: script requires bash 4+ (you have ${BASH_VERSION})." exit 1 fi diff --git a/src/nvim/ops.c b/src/nvim/ops.c index a70224f98b..755c1519fd 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -2748,6 +2748,10 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg) buf[1] = NUL; tv_dict_add_str(dict, S_LEN("operator"), buf); + // Selection type: visual or not. + tv_dict_add_special(dict, S_LEN("visual"), + oap->is_VIsual ? kSpecialVarTrue : kSpecialVarFalse); + tv_dict_set_keys_readonly(dict); textlock++; apply_autocmds(EVENT_TEXTYANKPOST, NULL, NULL, false, curbuf); diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index 954e5d875f..5217aa7339 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -1246,23 +1246,23 @@ func Test_TextYankPost() norm "ayiw call assert_equal( - \{'regcontents': ['foo'], 'inclusive': v:true, 'regname': 'a', 'operator': 'y', 'regtype': 'v'}, + \{'regcontents': ['foo'], 'inclusive': v:true, 'regname': 'a', 'operator': 'y', 'visual': v:false, 'regtype': 'v'}, \g:event) norm y_ call assert_equal( - \{'regcontents': ['foo'], 'inclusive': v:false, 'regname': '', 'operator': 'y', 'regtype': 'V'}, + \{'regcontents': ['foo'], 'inclusive': v:false, 'regname': '', 'operator': 'y', 'visual': v:false, 'regtype': 'V'}, \g:event) call feedkeys("\<C-V>y", 'x') call assert_equal( - \{'regcontents': ['f'], 'inclusive': v:true, 'regname': '', 'operator': 'y', 'regtype': "\x161"}, + \{'regcontents': ['f'], 'inclusive': v:true, 'regname': '', 'operator': 'y', 'visual': v:true, 'regtype': "\x161"}, \g:event) norm "xciwbar call assert_equal( - \{'regcontents': ['foo'], 'inclusive': v:true, 'regname': 'x', 'operator': 'c', 'regtype': 'v'}, + \{'regcontents': ['foo'], 'inclusive': v:true, 'regname': 'x', 'operator': 'c', 'visual': v:false, 'regtype': 'v'}, \g:event) norm "bdiw call assert_equal( - \{'regcontents': ['bar'], 'inclusive': v:true, 'regname': 'b', 'operator': 'd', 'regtype': 'v'}, + \{'regcontents': ['bar'], 'inclusive': v:true, 'regname': 'b', 'operator': 'd', 'visual': v:false, 'regtype': 'v'}, \g:event) call assert_equal({}, v:event) diff --git a/test/functional/autocmd/textyankpost_spec.lua b/test/functional/autocmd/textyankpost_spec.lua index 8c23b72cff..3898d59e58 100644 --- a/test/functional/autocmd/textyankpost_spec.lua +++ b/test/functional/autocmd/textyankpost_spec.lua @@ -27,7 +27,8 @@ describe('TextYankPost', function() operator = 'y', regcontents = { 'foo\nbar' }, regname = '', - regtype = 'V' + regtype = 'V', + visual = false }, eval('g:event')) eq(1, eval('g:count')) @@ -40,7 +41,8 @@ describe('TextYankPost', function() operator = 'y', regcontents = { 'baz ' }, regname = '', - regtype = 'v' + regtype = 'v', + visual = false }, eval('g:event')) eq(2, eval('g:count')) @@ -50,7 +52,8 @@ describe('TextYankPost', function() operator = 'y', regcontents = { 'foo', 'baz' }, regname = '', - regtype = "\0223" -- ^V + block width + regtype = "\0223", -- ^V + block width + visual = true }, eval('g:event')) eq(3, eval('g:count')) end) @@ -62,7 +65,8 @@ describe('TextYankPost', function() operator = 'y', regcontents = { 'foo\nbar' }, regname = '', - regtype = 'V' + regtype = 'V', + visual = false }, eval('g:event')) command('set debug=msg') @@ -92,7 +96,8 @@ describe('TextYankPost', function() operator = 'y', regcontents = { 'foo\nbar' }, regname = '', - regtype = 'V' + regtype = 'V', + visual = false }, eval('g:event')) eq(1, eval('g:count')) eq({ 'foo\nbar' }, funcs.getreg('+',1,1)) @@ -105,7 +110,8 @@ describe('TextYankPost', function() operator = 'd', regcontents = { 'foo' }, regname = '', - regtype = 'v' + regtype = 'v', + visual = false }, eval('g:event')) eq(1, eval('g:count')) @@ -115,7 +121,8 @@ describe('TextYankPost', function() operator = 'd', regcontents = { '\nbar' }, regname = '', - regtype = 'V' + regtype = 'V', + visual = false }, eval('g:event')) eq(2, eval('g:count')) @@ -125,7 +132,8 @@ describe('TextYankPost', function() operator = 'c', regcontents = { 'baz' }, regname = '', - regtype = 'v' + regtype = 'v', + visual = false }, eval('g:event')) eq(3, eval('g:count')) end) @@ -153,7 +161,8 @@ describe('TextYankPost', function() operator = 'y', regcontents = { 'bar' }, regname = 'b', - regtype = 'v' + regtype = 'v', + visual = false }, eval('g:event')) feed('"*yy') @@ -162,7 +171,8 @@ describe('TextYankPost', function() operator = 'y', regcontents = { 'foo\nbar' }, regname = '*', - regtype = 'V' + regtype = 'V', + visual = false }, eval('g:event')) command("set clipboard=unnamed") @@ -174,7 +184,8 @@ describe('TextYankPost', function() operator = 'y', regcontents = { 'foo\nbar' }, regname = '', - regtype = 'V' + regtype = 'V', + visual = false }, eval('g:event')) feed('"*yy') @@ -183,7 +194,8 @@ describe('TextYankPost', function() operator = 'y', regcontents = { 'foo\nbar' }, regname = '*', - regtype = 'V' + regtype = 'V', + visual = false }, eval('g:event')) end) @@ -194,7 +206,8 @@ describe('TextYankPost', function() operator = 'd', regcontents = { 'foo\nbar' }, regname = '+', - regtype = 'V' + regtype = 'V', + visual = false }, eval('g:event')) eq(1, eval('g:count')) @@ -204,7 +217,8 @@ describe('TextYankPost', function() operator = 'y', regcontents = { 'baz text' }, regname = '', - regtype = 'V' + regtype = 'V', + visual = false }, eval('g:event')) eq(2, eval('g:count')) @@ -214,7 +228,8 @@ describe('TextYankPost', function() operator = 'y', regcontents = { 'baz ' }, regname = '', - regtype = 'v' + regtype = 'v', + visual = false }, eval('g:event')) eq(3, eval('g:count')) @@ -224,7 +239,8 @@ describe('TextYankPost', function() operator = 'd', regcontents = { 'baz text' }, regname = '', - regtype = 'V' + regtype = 'V', + visual = false }, eval('g:event')) eq(4, eval('g:count')) end) diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua index 596b960419..aa0b3d822b 100644 --- a/test/functional/lua/vim_spec.lua +++ b/test/functional/lua/vim_spec.lua @@ -243,6 +243,8 @@ describe('lua stdlib', function() { "here be dragons", " ", false, { "here", "be", "dragons"} }, { "axaby", "ab?", false, { '', 'x', 'y' } }, { "f v2v v3v w2w ", "([vw])2%1", false, { 'f ', ' v3v ', ' ' } }, + { "", "", false, {} }, + { "", "a", false, { '' } }, { "x*yz*oo*l", "*", true, { 'x', 'yz', 'oo', 'l' } }, } diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index f1478c782d..ae436360c3 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -800,13 +800,14 @@ describe('LSP', function() make_edit(0, 0, 0, 0, {"123"}); make_edit(1, 0, 1, 1, {"2"}); make_edit(2, 0, 2, 2, {"3"}); + make_edit(3, 2, 3, 4, {""}); } exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1) eq({ '123First line of text'; '2econd line of text'; '3ird line of text'; - 'Fourth line of text'; + 'Foth line of text'; 'å å ɧ 汉语 ↥ 🤦 🦄'; }, buf_lines(1)) end) @@ -1317,4 +1318,75 @@ describe('LSP', function() eq("Unknown", exec_lua("return vim.lsp.util._get_symbol_kind_name(1000)")) end) end) + + describe('lsp.util.jump_to_location', function() + local target_bufnr + + before_each(function() + target_bufnr = exec_lua [[ + local bufnr = vim.uri_to_bufnr("file://fake/uri") + local lines = {"1st line of text", "å å ɧ 汉语 ↥ 🤦 🦄"} + vim.api.nvim_buf_set_lines(bufnr, 0, 1, false, lines) + return bufnr + ]] + end) + + local location = function(start_line, start_char, end_line, end_char) + return { + uri = "file://fake/uri", + range = { + start = { line = start_line, character = start_char }, + ["end"] = { line = end_line, character = end_char }, + }, + } + end + + local jump = function(msg) + eq(true, exec_lua('return vim.lsp.util.jump_to_location(...)', msg)) + eq(target_bufnr, exec_lua[[return vim.fn.bufnr('%')]]) + return { + line = exec_lua[[return vim.fn.line('.')]], + col = exec_lua[[return vim.fn.col('.')]], + } + end + + it('jumps to a Location', function() + local pos = jump(location(0, 9, 0, 9)) + eq(1, pos.line) + eq(10, pos.col) + end) + + it('jumps to a LocationLink', function() + local pos = jump({ + targetUri = "file://fake/uri", + targetSelectionRange = { + start = { line = 0, character = 4 }, + ["end"] = { line = 0, character = 4 }, + }, + targetRange = { + start = { line = 1, character = 5 }, + ["end"] = { line = 1, character = 5 }, + }, + }) + eq(1, pos.line) + eq(5, pos.col) + end) + + it('jumps to the correct multibyte column', function() + local pos = jump(location(1, 2, 1, 2)) + eq(2, pos.line) + eq(4, pos.col) + eq('å', exec_lua[[return vim.fn.expand('<cword>')]]) + end) + end) + + describe('lsp.util._make_floating_popup_size', function() + exec_lua [[ contents = + {"text tαxt txtα tex", + "text tααt tααt text", + "text tαxt tαxt"} + ]] + eq({19,3}, exec_lua[[ return {vim.lsp.util._make_floating_popup_size(contents)} ]]) + eq({15,5}, exec_lua[[ return {vim.lsp.util._make_floating_popup_size(contents,{width = 15, wrap_at = 14})} ]]) + end) end) |