aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--config/CMakeLists.txt2
-rw-r--r--runtime/doc/autocmd.txt1
-rw-r--r--runtime/doc/eval.txt2
-rw-r--r--runtime/doc/lsp.txt8
-rw-r--r--runtime/doc/lua.txt14
-rw-r--r--runtime/lua/vim/highlight.lua29
-rw-r--r--runtime/lua/vim/lsp.lua2
-rw-r--r--runtime/lua/vim/lsp/callbacks.lua11
-rw-r--r--runtime/lua/vim/lsp/protocol.lua22
-rw-r--r--runtime/lua/vim/lsp/util.lua213
-rw-r--r--runtime/lua/vim/shared.lua2
-rwxr-xr-xscripts/vim-patch.sh2
-rw-r--r--src/nvim/ops.c4
-rw-r--r--src/nvim/testdir/test_autocmd.vim10
-rw-r--r--test/functional/autocmd/textyankpost_spec.lua48
-rw-r--r--test/functional/lua/vim_spec.lua2
-rw-r--r--test/functional/plugin/lsp_spec.lua74
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)