aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/autoload/provider/pythonx.vim2
-rw-r--r--runtime/doc/editing.txt4
-rw-r--r--runtime/doc/eval.txt33
-rw-r--r--runtime/doc/lsp.txt3
-rw-r--r--runtime/doc/lua.txt89
-rw-r--r--runtime/doc/vim_diff.txt4
-rw-r--r--runtime/filetype.vim3
-rw-r--r--runtime/indent/testdir/runtest.vim1
-rw-r--r--runtime/lua/vim/highlight.lua41
-rw-r--r--runtime/lua/vim/lsp/buf.lua16
-rw-r--r--runtime/lua/vim/lsp/callbacks.lua52
-rw-r--r--runtime/lua/vim/lsp/log.lua4
-rw-r--r--runtime/lua/vim/lsp/protocol.lua10
-rw-r--r--runtime/lua/vim/lsp/util.lua106
-rw-r--r--runtime/lua/vim/shared.lua41
-rw-r--r--runtime/lua/vim/uri.lua10
-rw-r--r--runtime/pack/dist/opt/termdebug/plugin/termdebug.vim21
-rw-r--r--runtime/syntax/tutor.vim12
-rwxr-xr-xscripts/vim-patch.sh6
-rw-r--r--src/nvim/buffer_defs.h7
-rw-r--r--src/nvim/eval.c21
-rw-r--r--src/nvim/eval.h1
-rw-r--r--src/nvim/eval.lua8
-rw-r--r--src/nvim/eval/funcs.c63
-rw-r--r--src/nvim/ex_cmds2.c9
-rw-r--r--src/nvim/indent.c10
-rw-r--r--src/nvim/lua/vim.lua80
-rw-r--r--src/nvim/main.c2
-rw-r--r--src/nvim/message.c7
-rw-r--r--src/nvim/move.c2
-rw-r--r--src/nvim/option.c6
-rw-r--r--src/nvim/screen.c12
-rw-r--r--src/nvim/testdir/test_breakindent.vim59
-rw-r--r--src/nvim/testdir/test_cursor_func.vim15
-rw-r--r--src/nvim/testdir/test_filetype.vim1
-rw-r--r--src/nvim/testdir/test_match.vim44
-rw-r--r--src/nvim/testdir/test_options.vim7
-rw-r--r--src/nvim/testdir/test_startup.vim9
-rw-r--r--test/functional/core/startup_spec.lua26
-rw-r--r--test/functional/ex_cmds/profile_spec.lua76
-rw-r--r--test/functional/lua/uri_spec.lua23
-rw-r--r--test/functional/lua/vim_spec.lua207
-rw-r--r--test/functional/plugin/lsp_spec.lua179
43 files changed, 1146 insertions, 186 deletions
diff --git a/runtime/autoload/provider/pythonx.vim b/runtime/autoload/provider/pythonx.vim
index 23e7ff8f64..ffb9bf3021 100644
--- a/runtime/autoload/provider/pythonx.vim
+++ b/runtime/autoload/provider/pythonx.vim
@@ -29,7 +29,7 @@ endfunction
function! s:get_python_candidates(major_version) abort
return {
\ 2: ['python2', 'python2.7', 'python2.6', 'python'],
- \ 3: ['python3', 'python3.8', 'python3.7', 'python3.6', 'python3.5',
+ \ 3: ['python3', 'python3.9', 'python3.8', 'python3.7', 'python3.6', 'python3.5',
\ 'python3.4', 'python3.3', 'python']
\ }[a:major_version]
endfunction
diff --git a/runtime/doc/editing.txt b/runtime/doc/editing.txt
index 23a65f16e4..ac398ec494 100644
--- a/runtime/doc/editing.txt
+++ b/runtime/doc/editing.txt
@@ -1265,7 +1265,7 @@ exist, the next-higher scope in the hierarchy applies.
other tabs and windows is not changed.
*:tcd-*
-:tcd[!] - Change to the previous current directory (before the
+:tc[d][!] - Change to the previous current directory (before the
previous ":tcd {path}" command).
*:tch* *:tchdir*
@@ -1280,7 +1280,7 @@ exist, the next-higher scope in the hierarchy applies.
:lch[dir][!] Same as |:lcd|.
*:lcd-*
-:lcd[!] - Change to the previous current directory (before the
+:lc[d][!] - Change to the previous current directory (before the
previous ":lcd {path}" command).
*:pw* *:pwd* *E187*
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index 6f13b34876..214d815006 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -1423,6 +1423,10 @@ PREDEFINED VIM VARIABLES *vim-variable* *v:var* *v:*
*E963*
Some variables can be set by the user, but the type cannot be changed.
+ *v:argv* *argv-variable*
+v:argv The command line arguments Vim was invoked with. This is a
+ list of strings. The first item is the Vim command.
+
*v:beval_col* *beval_col-variable*
v:beval_col The number of the column, over which the mouse pointer is.
This is the byte index in the |v:beval_lnum| line.
@@ -2059,7 +2063,7 @@ chanclose({id}[, {stream}]) Number Closes a channel or one of its streams
chansend({id}, {data}) Number Writes {data} to channel
char2nr({expr}[, {utf8}]) Number ASCII/UTF8 value of first char in {expr}
cindent({lnum}) Number C indent for line {lnum}
-clearmatches() none clear all matches
+clearmatches([{win}]) none clear all matches
col({expr}) Number column nr of cursor or mark
complete({startcol}, {matches}) none set Insert mode completion
complete_add({expr}) Number add completion match
@@ -2167,7 +2171,7 @@ getjumplist([{winnr} [, {tabnr}]])
getline({lnum}) String line {lnum} of current buffer
getline({lnum}, {end}) List lines {lnum} to {end} of current buffer
getloclist({nr} [, {what}]) List list of location list items
-getmatches() List list of current matches
+getmatches([{win}]) List list of current matches
getpid() Number process ID of Vim
getpos({expr}) List position of cursor, mark, etc.
getqflist([{what}]) List list of quickfix items
@@ -2259,7 +2263,7 @@ matchadd({group}, {pattern}[, {priority}[, {id}]])
matchaddpos({group}, {list}[, {priority}[, {id}]])
Number highlight positions with {group}
matcharg({nr}) List arguments of |:match|
-matchdelete({id}) Number delete match identified by {id}
+matchdelete({id} [, {win}]) Number delete match identified by {id}
matchend({expr}, {pat}[, {start}[, {count}]])
Number position where {pat} ends in {expr}
matchlist({expr}, {pat}[, {start}[, {count}]])
@@ -2352,7 +2356,7 @@ setfperm({fname}, {mode} Number set {fname} file permissions to {mode}
setline({lnum}, {line}) Number set line {lnum} to {line}
setloclist({nr}, {list}[, {action}[, {what}]])
Number modify location list using {list}
-setmatches({list}) Number restore a list of matches
+setmatches({list} [, {win}]) Number restore a list of matches
setpos({expr}, {list}) Number set the {expr} position to {list}
setqflist({list}[, {action}[, {what}]]
Number modify quickfix list using {list}
@@ -2600,6 +2604,7 @@ argv([{nr} [, {winid}])
the whole |arglist| is returned.
The {winid} argument specifies the window ID, see |argc()|.
+ For the Vim command line arguments see |v:argv|.
assert_beeps({cmd}) *assert_beeps()*
Run {cmd} and add an error message to |v:errors| if it does
@@ -3005,9 +3010,11 @@ cindent({lnum}) *cindent()*
When {lnum} is invalid -1 is returned.
See |C-indenting|.
-clearmatches() *clearmatches()*
- Clears all matches previously defined for the current window
- by |matchadd()| and the |:match| commands.
+clearmatches([{win}]) *clearmatches()*
+ Clears all matches previously defined for the current window
+ by |matchadd()| and the |:match| commands.
+ If {win} is specified, use the window with this number or
+ window ID instead of the current window.
*col()*
col({expr}) The result is a Number, which is the byte index of the column
@@ -4602,7 +4609,7 @@ getloclist({nr},[, {what}]) *getloclist()*
field is applicable only when called from a location list
window.
-getmatches() *getmatches()*
+getmatches([{win}]) *getmatches()*
Returns a |List| with all matches previously defined for the
current window by |matchadd()| and the |:match| commands.
|getmatches()| is useful in combination with |setmatches()|,
@@ -5943,7 +5950,7 @@ matchadd({group}, {pattern}[, {priority}[, {id} [, {dict}]]])
Defines a pattern to be highlighted in the current window (a
"match"). It will be highlighted with {group}. Returns an
identification number (ID), which can be used to delete the
- match using |matchdelete()|.
+ match using |matchdelete()|. The ID is bound to the window.
Matching is case sensitive and magic, unless case sensitivity
or magicness are explicitly overridden in {pattern}. The
'magic', 'smartcase' and 'ignorecase' options are not used.
@@ -6043,11 +6050,13 @@ matcharg({nr}) *matcharg()*
Highlighting matches using the |:match| commands are limited
to three matches. |matchadd()| does not have this limitation.
-matchdelete({id}) *matchdelete()* *E802* *E803*
+matchdelete({id} [, {win}) *matchdelete()* *E802* *E803*
Deletes a match with ID {id} previously defined by |matchadd()|
or one of the |:match| commands. Returns 0 if successful,
otherwise -1. See example for |matchadd()|. All matches can
be deleted in one operation by |clearmatches()|.
+ If {win} is specified, use the window with this number or
+ window ID instead of the current window.
matchend({expr}, {pat} [, {start} [, {count}]]) *matchend()*
Same as |match()|, but return the index of first character
@@ -7420,11 +7429,13 @@ setloclist({nr}, {list}[, {action}[, {what}]]) *setloclist()*
only the items listed in {what} are set. Refer to |setqflist()|
for the list of supported keys in {what}.
-setmatches({list}) *setmatches()*
+setmatches({list} [, {win}]) *setmatches()*
Restores a list of matches saved by |getmatches() for the
current window|. Returns 0 if successful, otherwise -1. All
current matches are cleared before the list is restored. See
example for |getmatches()|.
+ If {win} is specified, use the window with this number or
+ window ID instead of the current window.
*setpos()*
setpos({expr}, {list})
diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt
index 249136f32f..9460e600e3 100644
--- a/runtime/doc/lsp.txt
+++ b/runtime/doc/lsp.txt
@@ -731,6 +731,9 @@ transform_schema_to_table()
==============================================================================
Lua module: vim.lsp.buf *lsp-buf*
+code_action({context}) *vim.lsp.buf.code_action()*
+ TODO: Documentation
+
completion({context}) *vim.lsp.buf.completion()*
TODO: Documentation
diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt
index 7f376cbbf0..2b83c35c90 100644
--- a/runtime/doc/lua.txt
+++ b/runtime/doc/lua.txt
@@ -693,6 +693,27 @@ identical identifiers, highlighting both as |hl-WarningMsg|: >
(eq? @WarningMsg.left @WarningMsg.right))
------------------------------------------------------------------------------
+VIM.HIGHLIGHT *lua-highlight*
+
+Nvim includes a function for highlighting a selection on yank (see for example
+https://github.com/machakann/vim-highlightedyank). To enable it, add
+>
+ au TextYankPost * silent! lua require'vim.highlight'.on_yank()
+<
+to your `init.vim`. You can customize the highlight group and the duration of
+the highlight via
+>
+ au TextYankPost * silent! lua require'vim.highlight'.on_yank("IncSearch", 500)
+<
+
+vim.highlight.on_yank([{higroup}, {timeout}, {event}])
+ *vim.highlight.on_yank()*
+ Highlights the yanked text. Optional arguments are the highlight group
+ to use ({higroup}, default `"IncSearch"`), the duration of highlighting
+ in milliseconds ({timeout}, default `500`), and the event structure
+ that is fired ({event}, default `vim.v.event`).
+
+------------------------------------------------------------------------------
VIM.REGEX *lua-regex*
Vim regexes can be used directly from lua. Currently they only allow
@@ -758,6 +779,14 @@ vim.empty_dict() *vim.empty_dict()*
Note: if numeric keys are added to the table, the metatable will be
ignored and the dict converted to a list/array anyway.
+vim.region({bufnr}, {pos1}, {pos2}, {type}, {inclusive}) *vim.region()*
+ Converts a selection specified by the buffer ({bufnr}), starting
+ position ({pos1}, a zero-indexed pair `{line1,column1}`), ending
+ position ({pos2}, same format as {pos1}), the type of the register
+ for the selection ({type}, see |regtype|), and a boolean indicating
+ whether the selection is inclusive or not, into a zero-indexed table
+ of linewise selections of the form `{linenr = {startcol, endcol}}` .
+
vim.rpcnotify({channel}, {method}[, {args}...]) *vim.rpcnotify()*
Sends {event} to {channel} via |RPC| and returns immediately.
If {channel} is 0, the event is broadcast to all channels.
@@ -850,6 +879,66 @@ vim.types *vim.types*
only contain values for these three types.
==============================================================================
+Vim Internal Variables *lua-vim-internal-variables*
+
+Built-in Vim dictionaries can be accessed and set idiomatically in Lua by each
+of the following tables.
+
+To set a value: >
+
+ vim.g.my_global_variable = 5
+<
+
+To read a value: >
+
+ print(vim.g.my_global_variable)
+<
+
+To delete a value: >
+
+ vim.g.my_global_variable = nil
+<
+
+vim.g *vim.g*
+ Table with values from |g:|
+ Keys with no values set will result in `nil`.
+
+vim.b *vim.b*
+ Gets a buffer-scoped (b:) variable for the current buffer.
+ Keys with no values set will result in `nil`.
+
+vim.w *vim.w*
+ Gets a window-scoped (w:) variable for the current window.
+ Keys with no values set will result in `nil`.
+
+vim.t *vim.t*
+ Gets a tabpage-scoped (t:) variable for the current table.
+ Keys with no values set will result in `nil`.
+
+vim.v *vim.v*
+ Gets a v: variable.
+ Keys with no values set will result in `nil`.
+
+
+Vim Internal Options *lua-vim-internal-options*
+
+Read, set and clear vim |options| in Lua by each of the following tables.
+
+
+vim.o *vim.o*
+ Table with values from |options|
+ Invalid keys will result in an error.
+
+vim.bo *vim.bo*
+ Gets a buffer-scoped option for the current buffer.
+ Invalid keys will result in an error.
+
+vim.wo *vim.wo*
+ Gets a window-scoped option for the current window.
+ Invalid keys will result in an error.
+
+
+==============================================================================
Lua module: vim *lua-vim*
inspect({object}, {options}) *vim.inspect()*
diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt
index 376375e4ef..24b562543e 100644
--- a/runtime/doc/vim_diff.txt
+++ b/runtime/doc/vim_diff.txt
@@ -116,6 +116,10 @@ backwards-compatibility cost. Some examples:
- Directories for 'directory' and 'undodir' are auto-created.
- Terminal features such as 'guicursor' are enabled where possible.
+Some features are built in that otherwise required external plugins:
+
+- Highlighting the yanked region, see |lua-highlight|.
+
ARCHITECTURE ~
External plugins run in separate processes. |remote-plugin| This improves
diff --git a/runtime/filetype.vim b/runtime/filetype.vim
index dc0bca2c60..0b5003dc44 100644
--- a/runtime/filetype.vim
+++ b/runtime/filetype.vim
@@ -1129,6 +1129,9 @@ au BufNewFile,BufRead pf.conf setf pf
" Pam conf
au BufNewFile,BufRead */etc/pam.conf setf pamconf
+" Pam environment
+au BufNewFile,BufRead pam_env.conf,.pam_environment setf pamenv
+
" PApp
au BufNewFile,BufRead *.papp,*.pxml,*.pxsl setf papp
diff --git a/runtime/indent/testdir/runtest.vim b/runtime/indent/testdir/runtest.vim
index 9502c42f3e..945c2753e9 100644
--- a/runtime/indent/testdir/runtest.vim
+++ b/runtime/indent/testdir/runtest.vim
@@ -10,6 +10,7 @@ filetype indent on
syn on
set nowrapscan
set report=9999
+set modeline
au! SwapExists * call HandleSwapExists()
func HandleSwapExists()
diff --git a/runtime/lua/vim/highlight.lua b/runtime/lua/vim/highlight.lua
new file mode 100644
index 0000000000..5c98c626a4
--- /dev/null
+++ b/runtime/lua/vim/highlight.lua
@@ -0,0 +1,41 @@
+local api = vim.api
+
+local highlight = {}
+
+--- 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
+function highlight.on_yank(higroup, timeout, event)
+ event = event or vim.v.event
+ if event.operator ~= 'y' or event.regtype == '' then return end
+ higroup = higroup or "IncSearch"
+ timeout = timeout or 500
+
+ local bufnr = api.nvim_get_current_buf()
+ local yank_ns = api.nvim_create_namespace('')
+ api.nvim_buf_clear_namespace(bufnr, yank_ns, 0, -1)
+
+ local pos1 = vim.fn.getpos("'[")
+ local pos2 = vim.fn.getpos("']")
+
+ 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
+
+ vim.defer_fn(
+ function() api.nvim_buf_clear_namespace(bufnr, yank_ns, 0, -1) end,
+ timeout
+ )
+end
+
+return highlight
diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua
index 0b45951a56..7a819f3c3d 100644
--- a/runtime/lua/vim/lsp/buf.lua
+++ b/runtime/lua/vim/lsp/buf.lua
@@ -161,5 +161,21 @@ function M.clear_references()
util.buf_clear_references()
end
+function M.code_action(context)
+ validate { context = { context, 't', true } }
+ context = context or { diagnostics = util.get_line_diagnostics() }
+ local params = util.make_range_params()
+ params.context = context
+ request('textDocument/codeAction', params)
+end
+
+function M.execute_command(command)
+ validate {
+ command = { command.command, 's' },
+ arguments = { command.arguments, 't', true }
+ }
+ request('workspace/executeCommand', command)
+end
+
return M
-- vim:sw=2 ts=2 et
diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua
index 70d21be8e7..7c51fc2cc2 100644
--- a/runtime/lua/vim/lsp/callbacks.lua
+++ b/runtime/lua/vim/lsp/callbacks.lua
@@ -3,6 +3,7 @@ local protocol = require 'vim.lsp.protocol'
local util = require 'vim.lsp.util'
local vim = vim
local api = vim.api
+local buf = require 'vim.lsp.buf'
local M = {}
@@ -11,13 +12,56 @@ local function err_message(...)
api.nvim_command("redraw")
end
+M['workspace/executeCommand'] = function(err, _)
+ if err then
+ error("Could not execute code action: "..err.message)
+ end
+end
+
+M['textDocument/codeAction'] = function(_, _, actions)
+ if actions == nil or vim.tbl_isempty(actions) then
+ print("No code actions available")
+ return
+ end
+
+ local option_strings = {"Code Actions:"}
+ for i, action in ipairs(actions) do
+ local title = action.title:gsub('\r\n', '\\r\\n')
+ title = title:gsub('\n', '\\n')
+ table.insert(option_strings, string.format("%d. %s", i, title))
+ end
+
+ local choice = vim.fn.inputlist(option_strings)
+ if choice < 1 or choice > #actions then
+ return
+ end
+ local action_chosen = actions[choice]
+ -- textDocument/codeAction can return either Command[] or CodeAction[].
+ -- If it is a CodeAction, it can have either an edit, a command or both.
+ -- Edits should be executed first
+ if action_chosen.edit or type(action_chosen.command) == "table" then
+ if action_chosen.edit then
+ util.apply_workspace_edit(action_chosen.edit)
+ end
+ if type(action_chosen.command) == "table" then
+ buf.execute_command(action_chosen.command)
+ end
+ else
+ buf.execute_command(action_chosen)
+ end
+end
+
M['workspace/applyEdit'] = function(_, _, workspace_edit)
if not workspace_edit then return end
-- TODO(ashkan) Do something more with label?
if workspace_edit.label then
print("Workspace edit", workspace_edit.label)
end
- util.apply_workspace_edit(workspace_edit.edit)
+ local status, result = pcall(util.apply_workspace_edit, workspace_edit.edit)
+ return {
+ applied = status;
+ failureReason = result;
+ }
end
M['textDocument/publishDiagnostics'] = function(_, _, result)
@@ -198,12 +242,12 @@ end
-- Add boilerplate error validation and logging for all of these.
for k, fn in pairs(M) do
- M[k] = function(err, method, params, client_id)
- local _ = log.debug() and log.debug('default_callback', method, { params = params, client_id = client_id, err = err })
+ M[k] = function(err, method, params, client_id, bufnr)
+ log.debug('default_callback', method, { params = params, client_id = client_id, err = err, bufnr = bufnr })
if err then
error(tostring(err))
end
- return fn(err, method, params, client_id)
+ return fn(err, method, params, client_id, bufnr)
end
end
diff --git a/runtime/lua/vim/lsp/log.lua b/runtime/lua/vim/lsp/log.lua
index c0db5e5485..696ce43a59 100644
--- a/runtime/lua/vim/lsp/log.lua
+++ b/runtime/lua/vim/lsp/log.lua
@@ -17,14 +17,14 @@ log.levels = {
-- Default log level is warn.
local current_log_level = log.levels.WARN
-local log_date_format = "%FT%H:%M:%SZ%z"
+local log_date_format = "%FT%H:%M:%S%z"
do
local path_sep = vim.loop.os_uname().sysname == "Windows" and "\\" or "/"
local function path_join(...)
return table.concat(vim.tbl_flatten{...}, path_sep)
end
- local logfilename = path_join(vim.fn.stdpath('data'), 'vim-lsp.log')
+ local logfilename = path_join(vim.fn.stdpath('data'), 'lsp.log')
--- Return the log filename.
function log.get_filename()
diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua
index 76817e3a4a..877d11411b 100644
--- a/runtime/lua/vim/lsp/protocol.lua
+++ b/runtime/lua/vim/lsp/protocol.lua
@@ -620,6 +620,15 @@ function protocol.make_client_capabilities()
-- Send textDocument/didSave after saving (BufWritePost)
didSave = true;
};
+ codeAction = {
+ dynamicRegistration = false;
+
+ codeActionLiteralSupport = {
+ codeActionKind = {
+ valueSet = {};
+ };
+ };
+ };
completion = {
dynamicRegistration = false;
completionItem = {
@@ -703,6 +712,7 @@ function protocol.make_client_capabilities()
};
hierarchicalWorkspaceSymbolSupport = true;
};
+ applyEdit = true;
};
experimental = nil;
}
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index 494eebf9ea..5c6d183ac1 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -94,18 +94,45 @@ 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)
+
+ if #lines > 0 then
+ return vim.str_byteindex(lines[1], cnum)
+ end
+ end
+
+ return cnum
+end
+
function M.apply_text_edits(text_edits, bufnr)
if not next(text_edits) then return end
+ if not api.nvim_buf_is_loaded(bufnr) then
+ vim.fn.bufload(bufnr)
+ end
local start_line, finish_line = math.huge, -1
local cleaned = {}
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 end_row = e.range["end"].line
+ local end_col = get_line_byte_from_line_character(
+ bufnr,
+ end_row,
+ e.range["end"].character)
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.
table.insert(cleaned, {
i = i;
- A = {e.range.start.line; e.range.start.character};
- B = {e.range["end"].line; e.range["end"].character};
+ A = {start_row; start_col};
+ B = {end_row; end_col};
lines = vim.split(e.newText, '\n', true);
})
end
@@ -113,9 +140,6 @@ function M.apply_text_edits(text_edits, bufnr)
-- Reverse sort the orders so we can apply them without interfering with
-- eachother. Also add i as a sort key to mimic a stable sort.
table.sort(cleaned, edit_sort_key)
- if not api.nvim_buf_is_loaded(bufnr) then
- vim.fn.bufload(bufnr)
- end
local lines = api.nvim_buf_get_lines(bufnr, start_line, finish_line + 1, false)
local fix_eol = api.nvim_buf_get_option(bufnr, 'fixeol')
local set_eol = fix_eol and api.nvim_buf_line_count(bufnr) <= finish_line + 1
@@ -159,10 +183,12 @@ end
function M.apply_text_document_edit(text_document_edit)
local text_document = text_document_edit.textDocument
local bufnr = vim.uri_to_bufnr(text_document.uri)
- -- `VersionedTextDocumentIdentifier`s version may be nil https://microsoft.github.io/language-server-protocol/specification#versionedTextDocumentIdentifier
- if text_document.version ~= vim.NIL and M.buf_versions[bufnr] > text_document.version then
- print("Buffer ", text_document.uri, " newer than edits.")
- return
+ if text_document.version then
+ -- `VersionedTextDocumentIdentifier`s version may be null https://microsoft.github.io/language-server-protocol/specification#versionedTextDocumentIdentifier
+ if text_document.version ~= vim.NIL and M.buf_versions[bufnr] ~= nil and M.buf_versions[bufnr] > text_document.version then
+ print("Buffer ", text_document.uri, " newer than edits.")
+ return
+ end
end
M.apply_text_edits(text_document_edit.edits, bufnr)
end
@@ -203,6 +229,13 @@ local function remove_unmatch_completion_items(items, prefix)
end, items)
end
+-- Acording to LSP spec, if the client set "completionItemKind.valueSet",
+-- the client must handle it properly even if it receives a value outside the specification.
+-- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion
+function M._get_completion_item_kind_name(completion_item_kind)
+ return protocol.CompletionItemKind[completion_item_kind] or "Unknown"
+end
+
--- Getting vim complete-items with incomplete flag.
-- @params CompletionItem[], CompletionList or nil (https://microsoft.github.io/language-server-protocol/specification#textDocument_completion)
-- @return { matches = complete-items table, incomplete = boolean }
@@ -234,7 +267,7 @@ function M.text_document_completion_list_to_complete_items(result, prefix)
table.insert(matches, {
word = word,
abbr = completion_item.label,
- kind = protocol.CompletionItemKind[completion_item.kind] or '',
+ kind = M._get_completion_item_kind_name(completion_item.kind),
menu = completion_item.detail or '',
info = info,
icase = 1,
@@ -436,8 +469,9 @@ function M.jump_to_location(location)
local items = {{tagname=vim.fn.expand('<cword>'), from=from}}
vim.fn.settagstack(vim.fn.win_getid(), {items=items}, 't')
- --- Jump to new location
+ --- Jump to new location (adjusting for UTF-16 encoding of characters)
api.nvim_set_current_buf(bufnr)
+ 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
@@ -701,20 +735,29 @@ do
return severity_highlights[severity]
end
- function M.show_line_diagnostics()
+ function M.get_line_diagnostics()
local bufnr = api.nvim_get_current_buf()
- local line = api.nvim_win_get_cursor(0)[1] - 1
+ local linenr = api.nvim_win_get_cursor(0)[1] - 1
+
+ local buffer_diagnostics = M.diagnostics_by_buf[bufnr]
+
+ if not buffer_diagnostics then
+ return {}
+ end
+
+ local diagnostics_by_line = M.diagnostics_group_by_line(buffer_diagnostics)
+ return diagnostics_by_line[linenr] or {}
+ end
+
+ function M.show_line_diagnostics()
-- local marks = api.nvim_buf_get_extmarks(bufnr, diagnostic_ns, {line, 0}, {line, -1}, {})
-- if #marks == 0 then
-- return
-- end
local lines = {"Diagnostics:"}
local highlights = {{0, "Bold"}}
-
- local buffer_diagnostics = M.diagnostics_by_buf[bufnr]
- if not buffer_diagnostics then return end
- local line_diagnostics = M.diagnostics_group_by_line(buffer_diagnostics)[line]
- if not line_diagnostics then return end
+ local line_diagnostics = M.get_line_diagnostics()
+ if vim.tbl_isempty(line_diagnostics) then return end
for i, diagnostic in ipairs(line_diagnostics) do
-- for i, mark in ipairs(marks) do
@@ -934,6 +977,13 @@ function M.set_qflist(items)
})
end
+-- Acording to LSP spec, if the client set "symbolKind.valueSet",
+-- the client must handle it properly even if it receives a value outside the specification.
+-- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentSymbol
+function M._get_symbol_kind_name(symbol_kind)
+ return protocol.SymbolKind[symbol_kind] or "Unknown"
+end
+
--- Convert symbols to quickfix list items
---
--@symbols DocumentSymbol[] or SymbolInformation[]
@@ -942,7 +992,7 @@ function M.symbols_to_items(symbols, bufnr)
for _, symbol in ipairs(_symbols) do
if symbol.location then -- SymbolInformation type
local range = symbol.location.range
- local kind = protocol.SymbolKind[symbol.kind]
+ local kind = M._get_symbol_kind_name(symbol.kind)
table.insert(_items, {
filename = vim.uri_to_fname(symbol.location.uri),
lnum = range.start.line + 1,
@@ -951,7 +1001,7 @@ function M.symbols_to_items(symbols, bufnr)
text = '['..kind..'] '..symbol.name,
})
elseif symbol.range then -- DocumentSymbole type
- local kind = protocol.SymbolKind[symbol.kind]
+ local kind = M._get_symbol_kind_name(symbol.kind)
table.insert(_items, {
-- bufnr = _bufnr,
filename = vim.api.nvim_buf_get_name(_bufnr),
@@ -1018,14 +1068,26 @@ function M.try_trim_markdown_code_blocks(lines)
end
local str_utfindex = vim.str_utfindex
-function M.make_position_params()
+local function make_position_param()
local row, col = unpack(api.nvim_win_get_cursor(0))
row = row - 1
local line = api.nvim_buf_get_lines(0, row, row+1, true)[1]
col = str_utfindex(line, col)
+ return { line = row; character = col; }
+end
+
+function M.make_position_params()
return {
textDocument = M.make_text_document_params();
- position = { line = row; character = col; }
+ position = make_position_param()
+ }
+end
+
+function M.make_range_params()
+ local position = make_position_param()
+ return {
+ textDocument = { uri = vim.uri_from_bufnr(0) },
+ range = { start = position; ["end"] = position; }
}
end
diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua
index d18fcfaf95..2135bfc837 100644
--- a/runtime/lua/vim/shared.lua
+++ b/runtime/lua/vim/shared.lua
@@ -200,16 +200,7 @@ function vim.tbl_isempty(t)
return next(t) == nil
end
---- Merges two or more map-like tables.
----
---@see |extend()|
----
---@param behavior Decides what to do if a key is found in more than one map:
---- - "error": raise an error
---- - "keep": use value from the leftmost map
---- - "force": use value from the rightmost map
---@param ... Two or more map-like tables.
-function vim.tbl_extend(behavior, ...)
+local function tbl_extend(behavior, deep_extend, ...)
if (behavior ~= 'error' and behavior ~= 'keep' and behavior ~= 'force') then
error('invalid "behavior": '..tostring(behavior))
end
@@ -228,7 +219,9 @@ function vim.tbl_extend(behavior, ...)
vim.validate{["after the second argument"] = {tbl,'t'}}
if tbl then
for k, v in pairs(tbl) do
- if behavior ~= 'force' and ret[k] ~= nil then
+ if type(v) == 'table' and deep_extend and not vim.tbl_islist(v) then
+ ret[k] = tbl_extend(behavior, true, ret[k] or vim.empty_dict(), v)
+ elseif behavior ~= 'force' and ret[k] ~= nil then
if behavior == 'error' then
error('key found in more than one map: '..k)
end -- Else behavior is "keep".
@@ -241,6 +234,32 @@ function vim.tbl_extend(behavior, ...)
return ret
end
+--- Merges two or more map-like tables.
+---
+--@see |extend()|
+---
+--@param behavior Decides what to do if a key is found in more than one map:
+--- - "error": raise an error
+--- - "keep": use value from the leftmost map
+--- - "force": use value from the rightmost map
+--@param ... Two or more map-like tables.
+function vim.tbl_extend(behavior, ...)
+ return tbl_extend(behavior, false, ...)
+end
+
+--- Merges recursively two or more map-like tables.
+---
+--@see |tbl_extend()|
+---
+--@param behavior Decides what to do if a key is found in more than one map:
+--- - "error": raise an error
+--- - "keep": use value from the leftmost map
+--- - "force": use value from the rightmost map
+--@param ... Two or more map-like tables.
+function vim.tbl_deep_extend(behavior, ...)
+ return tbl_extend(behavior, true, ...)
+end
+
--- Deep compare values for equality
function vim.deep_equal(a, b)
if a == b then return true end
diff --git a/runtime/lua/vim/uri.lua b/runtime/lua/vim/uri.lua
index e28cc9e20f..9c3535c676 100644
--- a/runtime/lua/vim/uri.lua
+++ b/runtime/lua/vim/uri.lua
@@ -65,9 +65,11 @@ local function uri_from_fname(path)
return table.concat(uri_parts)
end
+local URI_SCHEME_PATTERN = '^([a-zA-Z]+[a-zA-Z0-9+-.]*)://.*'
+
local function uri_from_bufnr(bufnr)
local fname = vim.api.nvim_buf_get_name(bufnr)
- local scheme = fname:match("^([a-z]+)://.*")
+ local scheme = fname:match(URI_SCHEME_PATTERN)
if scheme then
return fname
else
@@ -76,6 +78,10 @@ local function uri_from_bufnr(bufnr)
end
local function uri_to_fname(uri)
+ local scheme = assert(uri:match(URI_SCHEME_PATTERN), 'URI must contain a scheme: ' .. uri)
+ if scheme ~= 'file' then
+ return uri
+ end
uri = uri_decode(uri)
-- TODO improve this.
if is_windows_file_uri(uri) then
@@ -89,7 +95,7 @@ end
-- Return or create a buffer for a uri.
local function uri_to_bufnr(uri)
- local scheme = assert(uri:match("^([a-z]+)://.*"), 'Uri must contain a scheme: ' .. uri)
+ local scheme = assert(uri:match(URI_SCHEME_PATTERN), 'URI must contain a scheme: ' .. uri)
if scheme == 'file' then
return vim.fn.bufadd(uri_to_fname(uri))
else
diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
index aa2b69ad97..28dc3256c7 100644
--- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
+++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
@@ -645,7 +645,7 @@ func s:InstallCommands()
command Gdb call win_gotoid(s:gdbwin)
command Program call win_gotoid(s:ptywin)
command Source call s:GotoSourcewinOrCreateIt()
- " command Winbar call s:InstallWinbar()
+ command Winbar call s:InstallWinbar()
" TODO: can the K mapping be restored?
nnoremap K :Evaluate<CR>
@@ -655,6 +655,19 @@ endfunc
" let s:winbar_winids = []
+" Install the window toolbar in the current window.
+func s:InstallWinbar()
+ " if has('menu') && &mouse != ''
+ " nnoremenu WinBar.Step :Step<CR>
+ " nnoremenu WinBar.Next :Over<CR>
+ " nnoremenu WinBar.Finish :Finish<CR>
+ " nnoremenu WinBar.Cont :Continue<CR>
+ " nnoremenu WinBar.Stop :Stop<CR>
+ " nnoremenu WinBar.Eval :Evaluate<CR>
+ " call add(s:winbar_winids, win_getid(winnr()))
+ " endif
+endfunc
+
" Delete installed debugger commands in the current window.
func s:DeleteCommands()
delcommand Break
@@ -670,7 +683,7 @@ func s:DeleteCommands()
delcommand Gdb
delcommand Program
delcommand Source
- " delcommand Winbar
+ delcommand Winbar
nunmap K
@@ -940,7 +953,7 @@ func s:GotoSourcewinOrCreateIt()
if !win_gotoid(s:sourcewin)
new
let s:sourcewin = win_getid(winnr())
- " call s:InstallWinbar()
+ call s:InstallWinbar()
endif
endfunc
@@ -971,7 +984,7 @@ func s:HandleCursor(msg)
" TODO: find existing window
exe 'split ' . fnameescape(fname)
let s:sourcewin = win_getid(winnr())
- " call s:InstallWinbar()
+ call s:InstallWinbar()
else
exe 'edit ' . fnameescape(fname)
endif
diff --git a/runtime/syntax/tutor.vim b/runtime/syntax/tutor.vim
index 6305eef734..83ca547fdd 100644
--- a/runtime/syntax/tutor.vim
+++ b/runtime/syntax/tutor.vim
@@ -33,16 +33,16 @@ syn keyword tutorMarks Todo Note Tip Excersise
syn region tutorCodeblock matchgroup=Delimiter start=/^\~\{3}.*$/ end=/^\~\{3}/
-syn region tutorShell matchgroup=Delimiter start=/^\~\{3} sh\s*$/ end=/^\~\{3}/ keepend contains=@TUTORSHELL
+syn region tutorShell matchgroup=Delimiter start=/^\~\{3} sh\s*$/ end=/^\~\{3}/ keepend contains=@TUTORSHELL concealends
syn match tutorShellPrompt /\(^\s*\)\@<=[$#]/ contained containedin=tutorShell
-syn region tutorInlineCode matchgroup=Delimiter start=/\\\@<!`/ end=/\\\@<!\(`{\@!\|`\s\)/
+syn region tutorInlineCode matchgroup=Delimiter start=/\\\@<!`/ end=/\\\@<!\(`{\@!\|`\s\)/ concealends
-syn region tutorCommand matchgroup=Delimiter start=/^\~\{3} cmd\( :\)\?\s*$/ end=/^\~\{3}/ keepend contains=@VIM
-syn region tutorInlineCommand matchgroup=Delimiter start=/\\\@<!`\(.*{vim}\)\@=/ end=/\\\@<!`\({vim}\)\@=/ nextgroup=tutorInlineType contains=@VIM
+syn region tutorCommand matchgroup=Delimiter start=/^\~\{3} cmd\( :\)\?\s*$/ end=/^\~\{3}/ keepend contains=@VIM concealends
+syn region tutorInlineCommand matchgroup=Delimiter start=/\\\@<!`\(.*`{vim}\)\@=/ end=/\\\@<!`\({vim}\)\@=/ nextgroup=tutorInlineType contains=@VIM concealends keepend
-syn region tutorNormal matchgroup=Delimiter start=/^\~\{3} norm\(al\?\)\?\s*$/ end=/^\~\{3}/ contains=@VIMNORMAL
-syn region tutorInlineNormal matchgroup=Delimiter start=/\\\@<!`\(\S*{normal}\)\@=/ end=/\\\@<!`\({normal}\)\@=/ nextgroup=tutorInlineType contains=@VIMNORMAL
+syn region tutorNormal matchgroup=Delimiter start=/^\~\{3} norm\(al\?\)\?\s*$/ end=/^\~\{3}/ contains=@VIMNORMAL concealends
+syn region tutorInlineNormal matchgroup=Delimiter start=/\\\@<!`\(\S*`{normal}\)\@=/ end=/\\\@<!`\({normal}\)\@=/ nextgroup=tutorInlineType contains=@VIMNORMAL concealends keepend
syn match tutorInlineType /{\(normal\|vim\)}/ contained conceal
diff --git a/scripts/vim-patch.sh b/scripts/vim-patch.sh
index dc62c9e744..a8b622d5c4 100755
--- a/scripts/vim-patch.sh
+++ b/scripts/vim-patch.sh
@@ -29,6 +29,7 @@ usage() {
echo " -h Show this message and exit."
echo " -l [git-log opts] List missing Vim patches."
echo " -L [git-log opts] List missing Vim patches (for scripts)."
+ echo " -m {vim-revision} List previous (older) missing Vim patches."
echo " -M List all merged patch-numbers (at current v:version)."
echo " -p {vim-revision} Download and generate a Vim patch. vim-revision"
echo " can be a Vim version (8.0.xxx) or a Git hash."
@@ -433,7 +434,7 @@ _set_tokens_and_tags() {
}
# Prints a newline-delimited list of Vim commits, for use by scripts.
-# "$1": use extended format?
+# "$1": use extended format? (with subject)
# "$@" is passed to list_vim_commits, as extra arguments to git-log.
list_missing_vimpatches() {
local -a missing_vim_patches=()
@@ -447,6 +448,8 @@ list_missing_vimpatches() {
# Sets / appends to missing_vim_patches (useful to avoid a subshell when
# used multiple times to cache tokens/vim_commit_tags).
+# "$1": use extended format? (with subject)
+# "$@": extra arguments to git-log.
_set_missing_vimpatches() {
local token vim_commit vim_tag patch_number
declare -a git_log_args
@@ -587,6 +590,7 @@ list_missing_previous_vimpatches_for_patch() {
set +u # Avoid "unbound variable" with bash < 4.4 below.
if [[ -z "${missing_list[*]}" ]]; then
msg_ok 'no missing previous Vim patches'
+ set -u
return 0
fi
set -u
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index b87a2ba721..d696eedbb7 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -1330,12 +1330,13 @@ struct window_S {
uint32_t w_p_fde_flags; // flags for 'foldexpr'
uint32_t w_p_fdt_flags; // flags for 'foldtext'
int *w_p_cc_cols; // array of columns to highlight or NULL
- int w_p_brimin; // minimum width for breakindent
- int w_p_brishift; // additional shift for breakindent
- bool w_p_brisbr; // sbr in 'briopt'
long w_p_siso; // 'sidescrolloff' local value
long w_p_so; // 'scrolloff' local value
+ int w_briopt_min; // minimum width for breakindent
+ int w_briopt_shift; // additional shift for breakindent
+ bool w_briopt_sbr; // sbr in 'briopt'
+
// transform a pointer to a "onebuf" option into a "allbuf" option
#define GLOBAL_WO(p) ((char *)p + sizeof(winopt_T))
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index ced3613005..017d46e802 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -63,6 +63,7 @@ static char *e_missbrac = N_("E111: Missing ']'");
static char *e_dictrange = N_("E719: Cannot use [:] with a Dictionary");
static char *e_illvar = N_("E461: Illegal variable name: %s");
static char *e_cannot_mod = N_("E995: Cannot modify existing variable");
+static char *e_invalwindow = N_("E957: Invalid window number");
// TODO(ZyX-I): move to eval/executor
static char *e_letwrong = N_("E734: Wrong variable type for %s=");
@@ -229,6 +230,7 @@ static struct vimvar {
VV(VV_ECHOSPACE, "echospace", VAR_NUMBER, VV_RO),
VV(VV_EXITING, "exiting", VAR_NUMBER, VV_RO),
VV(VV_LUA, "lua", VAR_PARTIAL, VV_RO),
+ VV(VV_ARGV, "argv", VAR_LIST, VV_RO),
};
#undef VV
@@ -6778,7 +6780,7 @@ int matchadd_dict_arg(typval_T *tv, const char **conceal_char,
if ((di = tv_dict_find(tv->vval.v_dict, S_LEN("window"))) != NULL) {
*win = find_win_by_nr_or_id(&di->di_tv);
if (*win == NULL) {
- EMSG(_("E957: Invalid window number"));
+ EMSG(_(e_invalwindow));
return FAIL;
}
}
@@ -8109,6 +8111,23 @@ void set_vim_var_dict(const VimVarIndex idx, dict_T *const val)
}
}
+/// Set the v:argv list.
+void set_argv_var(char **argv, int argc)
+{
+ list_T *l = tv_list_alloc(argc);
+ int i;
+
+ if (l == NULL) {
+ getout(1);
+ }
+ tv_list_set_lock(l, VAR_FIXED);
+ for (i = 0; i < argc; i++) {
+ tv_list_append_string(l, (const char *const)argv[i], -1);
+ TV_LIST_ITEM_TV(tv_list_last(l))->v_lock = VAR_FIXED;
+ }
+ set_vim_var_list(VV_ARGV, l);
+}
+
/*
* Set v:register if needed.
*/
diff --git a/src/nvim/eval.h b/src/nvim/eval.h
index ebc0eb0b1a..0b4cbb3b4d 100644
--- a/src/nvim/eval.h
+++ b/src/nvim/eval.h
@@ -159,6 +159,7 @@ typedef enum {
VV_ECHOSPACE,
VV_EXITING,
VV_LUA,
+ VV_ARGV,
} VimVarIndex;
/// All recognized msgpack types
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index 65c4cfe553..51872a7ba8 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -64,7 +64,7 @@ return {
chansend={args=2},
char2nr={args={1, 2}},
cindent={args=1},
- clearmatches={},
+ clearmatches={args={0, 1}},
col={args=1},
complete={args=2},
complete_add={args=1},
@@ -149,7 +149,7 @@ return {
getjumplist={args={0, 2}},
getline={args={1, 2}},
getloclist={args={1, 2}},
- getmatches={},
+ getmatches={args={0, 1}},
getpid={},
getpos={args=1},
getqflist={args={0, 1}},
@@ -227,7 +227,7 @@ return {
matchadd={args={2, 5}},
matchaddpos={args={2, 5}},
matcharg={args=1},
- matchdelete={args=1},
+ matchdelete={args={1, 2}},
matchend={args={2, 4}},
matchlist={args={2, 4}},
matchstr={args={2, 4}},
@@ -293,7 +293,7 @@ return {
setfperm={args=2},
setline={args=2},
setloclist={args={2, 4}},
- setmatches={args=1},
+ setmatches={args={1, 2}},
setpos={args=2},
setqflist={args={1, 3}},
setreg={args={2, 3}},
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index ac28e3c3e0..25beb4be50 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -93,6 +93,7 @@ PRAGMA_DIAG_POP
static char *e_listarg = N_("E686: Argument of %s must be a List");
static char *e_stringreq = N_("E928: String required");
+static char *e_invalwindow = N_("E957: Invalid window number");
/// Dummy va_list for passing to vim_snprintf
///
@@ -952,12 +953,30 @@ static void f_cindent(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = -1;
}
+static win_T * get_optional_window(typval_T *argvars, int idx)
+{
+ win_T *win = curwin;
+
+ if (argvars[idx].v_type != VAR_UNKNOWN) {
+ win = find_win_by_nr_or_id(&argvars[idx]);
+ if (win == NULL) {
+ EMSG(_(e_invalwindow));
+ return NULL;
+ }
+ }
+ return win;
+}
+
/*
* "clearmatches()" function
*/
static void f_clearmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- clear_matches(curwin);
+ win_T *win = get_optional_window(argvars, 0);
+
+ if (win != NULL) {
+ clear_matches(win);
+ }
}
/*
@@ -3452,10 +3471,16 @@ static void f_getloclist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- matchitem_T *cur = curwin->w_match_head;
+ matchitem_T *cur;
int i;
+ win_T *win = get_optional_window(argvars, 0);
+
+ if (win == NULL) {
+ return;
+ }
tv_list_alloc_ret(rettv, kListLenMayKnow);
+ cur = win->w_match_head;
while (cur != NULL) {
dict_T *dict = tv_dict_alloc();
if (cur->match.regprog == NULL) {
@@ -5771,8 +5796,13 @@ static void f_matcharg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_matchdelete(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- rettv->vval.v_number = match_delete(curwin,
- (int)tv_get_number(&argvars[0]), true);
+ win_T *win = get_optional_window(argvars, 1);
+ if (win == NULL) {
+ rettv->vval.v_number = -1;
+ } else {
+ rettv->vval.v_number = match_delete(win,
+ (int)tv_get_number(&argvars[0]), true);
+ }
}
/*
@@ -6393,20 +6423,14 @@ static void f_readdir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
os_closedir(&dir);
}
- rettv->vval.v_list = tv_list_alloc(kListLenShouldKnow);
- if (rettv->vval.v_list != NULL) {
- tv_list_ref(rettv->vval.v_list);
+ if (rettv->vval.v_list != NULL && ga.ga_len > 0) {
sort_strings((char_u **)ga.ga_data, ga.ga_len);
for (int i = 0; i < ga.ga_len; i++) {
path = ((const char **)ga.ga_data)[i];
tv_list_append_string(rettv->vval.v_list, path, -1);
}
}
- for (int i = 0; i < ga.ga_len; i++) {
- xfree(((uint8_t **)ga.ga_data)[i]);
- }
-
- ga_clear(&ga);
+ ga_clear_strings(&ga);
}
/*
@@ -8142,14 +8166,19 @@ static void f_setloclist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- dict_T *d;
- list_T *s = NULL;
+ dict_T *d;
+ list_T *s = NULL;
+ win_T *win = get_optional_window(argvars, 1);
rettv->vval.v_number = -1;
if (argvars[0].v_type != VAR_LIST) {
EMSG(_(e_listreq));
return;
}
+ if (win == NULL) {
+ return;
+ }
+
list_T *const l = argvars[0].vval.v_list;
// To some extent make sure that we are dealing with a list from
// "getmatches()".
@@ -8173,7 +8202,7 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
li_idx++;
});
- clear_matches(curwin);
+ clear_matches(win);
bool match_add_failed = false;
TV_LIST_ITER_CONST(l, li, {
int i = 0;
@@ -8219,13 +8248,13 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
? tv_get_string(&conceal_di->di_tv)
: NULL);
if (i == 0) {
- if (match_add(curwin, group,
+ if (match_add(win, group,
tv_dict_get_string(d, "pattern", false),
priority, id, NULL, conceal) != id) {
match_add_failed = true;
}
} else {
- if (match_add(curwin, group, NULL, priority, id, s, conceal) != id) {
+ if (match_add(win, group, NULL, priority, id, s, conceal) != id) {
match_add_failed = true;
}
tv_list_unref(s);
diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c
index 821227ec7a..7f4b01e306 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -1038,16 +1038,17 @@ static void profile_reset(void)
if (!HASHITEM_EMPTY(hi)) {
n--;
ufunc_T *uf = HI2UF(hi);
- if (uf->uf_profiling) {
+ if (uf->uf_prof_initialized) {
uf->uf_profiling = 0;
uf->uf_tm_count = 0;
uf->uf_tm_total = profile_zero();
uf->uf_tm_self = profile_zero();
uf->uf_tm_children = profile_zero();
- XFREE_CLEAR(uf->uf_tml_count);
- XFREE_CLEAR(uf->uf_tml_total);
- XFREE_CLEAR(uf->uf_tml_self);
+ for (int i = 0; i < uf->uf_lines.ga_len; i++) {
+ uf->uf_tml_count[i] = 0;
+ uf->uf_tml_total[i] = uf->uf_tml_self[i] = 0;
+ }
uf->uf_tml_start = profile_zero();
uf->uf_tml_children = profile_zero();
diff --git a/src/nvim/indent.c b/src/nvim/indent.c
index 7f17fb0035..fb277b25fd 100644
--- a/src/nvim/indent.c
+++ b/src/nvim/indent.c
@@ -398,10 +398,10 @@ int get_breakindent_win(win_T *wp, const char_u *line)
prev_tick = buf_get_changedtick(wp->w_buffer);
prev_indent = get_indent_str(line, (int)wp->w_buffer->b_p_ts, wp->w_p_list);
}
- bri = prev_indent + wp->w_p_brishift;
+ bri = prev_indent + wp->w_briopt_shift;
// indent minus the length of the showbreak string
- if (wp->w_p_brisbr) {
+ if (wp->w_briopt_sbr) {
bri -= vim_strsize(p_sbr);
}
// Add offset for number column, if 'n' is in 'cpoptions'
@@ -410,11 +410,11 @@ int get_breakindent_win(win_T *wp, const char_u *line)
// never indent past left window margin
if (bri < 0) {
bri = 0;
- } else if (bri > eff_wwidth - wp->w_p_brimin) {
+ } else if (bri > eff_wwidth - wp->w_briopt_min) {
// always leave at least bri_min characters on the left,
// if text width is sufficient
- bri = (eff_wwidth - wp->w_p_brimin < 0)
- ? 0 : eff_wwidth - wp->w_p_brimin;
+ bri = (eff_wwidth - wp->w_briopt_min < 0)
+ ? 0 : eff_wwidth - wp->w_briopt_min;
}
return bri;
diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua
index fc337c5e7e..523d23ec12 100644
--- a/src/nvim/lua/vim.lua
+++ b/src/nvim/lua/vim.lua
@@ -332,9 +332,26 @@ do
return pcall_ret(pcall(fn, ...))
end
end
+
+ vim.b = make_meta_accessor(
+ nil_wrap(function(v) return a.nvim_buf_get_var(0, v) end),
+ function(v, k) return a.nvim_buf_set_var(0, v, k) end,
+ function(v) return a.nvim_buf_del_var(0, v) end
+ )
+ vim.w = make_meta_accessor(
+ nil_wrap(function(v) return a.nvim_win_get_var(0, v) end),
+ function(v, k) return a.nvim_win_set_var(0, v, k) end,
+ function(v) return a.nvim_win_del_var(0, v) end
+ )
+ vim.t = make_meta_accessor(
+ nil_wrap(function(v) return a.nvim_tabpage_get_var(0, v) end),
+ function(v, k) return a.nvim_tabpage_set_var(0, v, k) end,
+ function(v) return a.nvim_tabpage_del_var(0, v) end
+ )
vim.g = make_meta_accessor(nil_wrap(a.nvim_get_var), a.nvim_set_var, a.nvim_del_var)
vim.v = make_meta_accessor(nil_wrap(a.nvim_get_vvar), a.nvim_set_vvar)
vim.o = make_meta_accessor(a.nvim_get_option, a.nvim_set_option)
+
local function getenv(k)
local v = vim.fn.getenv(k)
if v == vim.NIL then
@@ -398,4 +415,67 @@ do
vim.wo = new_win_opt_accessor(nil)
end
+--- Get a table of lines with start, end columns for a region marked by two points
+---
+--@param bufnr number of buffer
+--@param pos1 (line, column) tuple marking beginning of region
+--@param pos2 (line, column) tuple marking end of region
+--@param regtype type of selection (:help setreg)
+--@param inclusive boolean indicating whether the selection is end-inclusive
+--@return region lua table of the form {linenr = {startcol,endcol}}
+function vim.region(bufnr, pos1, pos2, regtype, inclusive)
+ if not vim.api.nvim_buf_is_loaded(bufnr) then
+ vim.fn.bufload(bufnr)
+ end
+
+ -- in case of block selection, columns need to be adjusted for non-ASCII characters
+ -- TODO: handle double-width characters
+ local bufline
+ if regtype:byte() == 22 then
+ bufline = vim.api.nvim_buf_get_lines(bufnr, pos1[1], pos1[1] + 1, true)[1]
+ pos1[2] = vim.str_utfindex(bufline, pos1[2])
+ end
+
+ local region = {}
+ for l = pos1[1], pos2[1] do
+ local c1, c2
+ if regtype:byte() == 22 then -- block selection: take width from regtype
+ c1 = pos1[2]
+ c2 = c1 + regtype:sub(2)
+ -- and adjust for non-ASCII characters
+ bufline = vim.api.nvim_buf_get_lines(bufnr, l, l + 1, true)[1]
+ if c1 < #bufline then
+ c1 = vim.str_byteindex(bufline, c1)
+ end
+ if c2 < #bufline then
+ c2 = vim.str_byteindex(bufline, c2)
+ end
+ else
+ c1 = (l == pos1[1]) and (pos1[2]) or 0
+ c2 = (l == pos2[1]) and (pos2[2] + (inclusive and 1 or 0)) or -1
+ end
+ table.insert(region, l, {c1, c2})
+ end
+ return region
+end
+
+--- Defers calling `fn` until `timeout` ms passes.
+---
+--- Use to do a one-shot timer that calls `fn`
+--@param fn Callback to call once `timeout` expires
+--@param timeout Number of milliseconds to wait before calling `fn`
+--@return timer luv timer object
+function vim.defer_fn(fn, timeout)
+ vim.validate { fn = { fn, 'c', true}; }
+ local timer = vim.loop.new_timer()
+ timer:start(timeout, 0, vim.schedule_wrap(function()
+ timer:stop()
+ timer:close()
+
+ fn()
+ end))
+
+ return timer
+end
+
return module
diff --git a/src/nvim/main.c b/src/nvim/main.c
index 4a9f2371a2..6ac9cdfbae 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -269,6 +269,8 @@ int main(int argc, char **argv)
early_init();
+ set_argv_var(argv, argc); // set v:argv
+
// Check if we have an interactive window.
check_and_set_isatty(&params);
diff --git a/src/nvim/message.c b/src/nvim/message.c
index a12e665099..9aa588e035 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -2574,10 +2574,15 @@ static int do_more_prompt(int typed_char)
msgchunk_T *mp;
int i;
+ // If headless mode is enabled and no input is required, this variable
+ // will be true. However If server mode is enabled, the message "--more--"
+ // should be displayed.
+ bool no_need_more = headless_mode && !embedded_mode;
+
// We get called recursively when a timer callback outputs a message. In
// that case don't show another prompt. Also when at the hit-Enter prompt
// and nothing was typed.
- if (entered || (State == HITRETURN && typed_char == 0)) {
+ if (no_need_more || entered || (State == HITRETURN && typed_char == 0)) {
return false;
}
entered = true;
diff --git a/src/nvim/move.c b/src/nvim/move.c
index d4f82bc601..8a8a639a52 100644
--- a/src/nvim/move.c
+++ b/src/nvim/move.c
@@ -996,7 +996,7 @@ void textpos2screenpos(win_T *wp, pos_T *pos, int *rowp, int *scolp,
col -= wp->w_leftcol;
- if (col >= 0 && col < width) {
+ if (col >= 0 && col < wp->w_width) {
coloff = col - scol + (local ? 0 : wp->w_wincol) + 1;
} else {
scol = ccol = ecol = 0;
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 84ace55d91..96f8e1529a 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -7229,9 +7229,9 @@ static bool briopt_check(win_T *wp)
}
}
- wp->w_p_brishift = bri_shift;
- wp->w_p_brimin = bri_min;
- wp->w_p_brisbr = bri_sbr;
+ wp->w_briopt_shift = bri_shift;
+ wp->w_briopt_min = bri_min;
+ wp->w_briopt_sbr = bri_sbr;
return true;
}
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index 8f8bfee60c..ba52f5b489 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -2966,11 +2966,11 @@ win_line (
}
}
- if (wp->w_p_brisbr && draw_state == WL_BRI - 1
+ if (wp->w_briopt_sbr && draw_state == WL_BRI - 1
&& n_extra == 0 && *p_sbr != NUL) {
// draw indent after showbreak value
draw_state = WL_BRI;
- } else if (wp->w_p_brisbr && draw_state == WL_SBR && n_extra == 0) {
+ } else if (wp->w_briopt_sbr && draw_state == WL_SBR && n_extra == 0) {
// after the showbreak, draw the breakindent
draw_state = WL_BRI - 1;
}
@@ -2994,7 +2994,13 @@ win_line (
c_final = NUL;
n_extra =
get_breakindent_win(wp, ml_get_buf(wp->w_buffer, lnum, false));
- if (wp->w_skipcol > 0 && wp->w_p_wrap) {
+ if (row == startrow) {
+ n_extra -= win_col_off2(wp);
+ if (n_extra < 0) {
+ n_extra = 0;
+ }
+ }
+ if (wp->w_skipcol > 0 && wp->w_p_wrap && wp->w_briopt_sbr) {
need_showbreak = false;
}
// Correct end of highlighted area for 'breakindent',
diff --git a/src/nvim/testdir/test_breakindent.vim b/src/nvim/testdir/test_breakindent.vim
index 6d88f1dc5a..a4c1f62a43 100644
--- a/src/nvim/testdir/test_breakindent.vim
+++ b/src/nvim/testdir/test_breakindent.vim
@@ -361,5 +361,64 @@ func Test_breakindent19_sbr_nextpage()
\ "> aaaaaaaaaaaaaaaaaa",
\ ]
call s:compare_lines(expect, lines)
+
+ setl breakindent briopt=min:18 sbr=>
+ norm! 5gj
+ let lines = s:screen_lines(1, 20)
+ let expect = [
+ \ ">aaaaaaaaaaaaaaaaaaa",
+ \ ">aaaaaaaaaaaaaaaaaaa",
+ \ ">aaaaaaaaaaaaaaaaaaa",
+ \ ]
+ call s:compare_lines(expect, lines)
call s:close_windows('set breakindent& briopt& sbr&')
endfunc
+
+func Test_breakindent20_cpo_n_nextpage()
+ let s:input = ""
+ call s:test_windows('setl breakindent briopt=min:14 cpo+=n number')
+ call setline(1, repeat('a', 200))
+ norm! 1gg
+ redraw!
+ let lines = s:screen_lines(1, 20)
+ let expect = [
+ \ " 1 aaaaaaaaaaaaaaaa",
+ \ " aaaaaaaaaaaaaaaa",
+ \ " aaaaaaaaaaaaaaaa",
+ \ ]
+ call s:compare_lines(expect, lines)
+ " Scroll down one screen line
+ setl scrolloff=5
+ norm! 5gj
+ redraw!
+ let lines = s:screen_lines(1, 20)
+ let expect = [
+ \ "--1 aaaaaaaaaaaaaaaa",
+ \ " aaaaaaaaaaaaaaaa",
+ \ " aaaaaaaaaaaaaaaa",
+ \ ]
+ call s:compare_lines(expect, lines)
+
+ setl briopt+=shift:2
+ norm! 1gg
+ let lines = s:screen_lines(1, 20)
+ let expect = [
+ \ " 1 aaaaaaaaaaaaaaaa",
+ \ " aaaaaaaaaaaaaa",
+ \ " aaaaaaaaaaaaaa",
+ \ ]
+ call s:compare_lines(expect, lines)
+ " Scroll down one screen line
+ norm! 5gj
+ let lines = s:screen_lines(1, 20)
+ let expect = [
+ \ "--1 aaaaaaaaaaaaaa",
+ \ " aaaaaaaaaaaaaa",
+ \ " aaaaaaaaaaaaaa",
+ \ ]
+ call s:compare_lines(expect, lines)
+
+ call s:close_windows('set breakindent& briopt& cpo& number&')
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_cursor_func.vim b/src/nvim/testdir/test_cursor_func.vim
index e8e561dfd8..2e190911b2 100644
--- a/src/nvim/testdir/test_cursor_func.vim
+++ b/src/nvim/testdir/test_cursor_func.vim
@@ -93,3 +93,18 @@ func Test_screenpos()
close
bwipe!
endfunc
+
+func Test_screenpos_number()
+ rightbelow new
+ rightbelow 73vsplit
+ call setline (1, repeat('x', 66))
+ setlocal number
+ redraw
+ let winid = win_getid()
+ let [winrow, wincol] = win_screenpos(winid)
+ let pos = screenpos(winid, 1, 66)
+ call assert_equal(winrow, pos.row)
+ call assert_equal(wincol + 66 + 3, pos.col)
+ close
+ bwipe!
+endfunc
diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim
index a9984acdd9..ace56a375f 100644
--- a/src/nvim/testdir/test_filetype.vim
+++ b/src/nvim/testdir/test_filetype.vim
@@ -321,6 +321,7 @@ let s:filename_checks = {
\ 'openroad': ['file.or'],
\ 'ora': ['file.ora'],
\ 'pamconf': ['/etc/pam.conf'],
+ \ 'pamenv': ['/etc/security/pam_env.conf', '/home/user/.pam_environment'],
\ 'papp': ['file.papp', 'file.pxml', 'file.pxsl'],
\ 'pascal': ['file.pas', 'file.dpr'],
\ 'passwd': ['any/etc/passwd', 'any/etc/passwd-', 'any/etc/passwd.edit', 'any/etc/shadow', 'any/etc/shadow-', 'any/etc/shadow.edit', 'any/var/backups/passwd.bak', 'any/var/backups/shadow.bak'],
diff --git a/src/nvim/testdir/test_match.vim b/src/nvim/testdir/test_match.vim
index c134cfb1c0..09448ca71b 100644
--- a/src/nvim/testdir/test_match.vim
+++ b/src/nvim/testdir/test_match.vim
@@ -149,6 +149,21 @@ function Test_match()
highlight MyGroup3 NONE
endfunc
+func Test_match_error()
+ call assert_fails('match Error', 'E475:')
+ call assert_fails('match Error /', 'E475:')
+ call assert_fails('4match Error /x/', 'E476:')
+ call assert_fails('match Error /x/ x', 'E488:')
+endfunc
+
+func Test_matchadd_error()
+ call assert_fails("call matchadd('GroupDoesNotExist', 'X')", 'E28:')
+ call assert_fails("call matchadd('Search', '\\(')", 'E475:')
+ call assert_fails("call matchadd('Search', 'XXX', 1, 123, 1)", 'E715:')
+ call assert_fails("call matchadd('Error', 'XXX', 1, 3)", 'E798:')
+ call assert_fails("call matchadd('Error', 'XXX', 1, 0)", 'E799:')
+endfunc
+
func Test_matchaddpos()
syntax on
set hlsearch
@@ -217,6 +232,19 @@ func Test_matchaddpos_otherwin()
call assert_equal(screenattr(1,2), screenattr(2,2))
call assert_notequal(screenattr(1,2), screenattr(1,4))
+ let savematches = getmatches(winid)
+ let expect = [
+ \ {'group': 'Search', 'pattern': '4', 'priority': 10, 'id': 4},
+ \ {'group': 'Error', 'id': 5, 'priority': 10, 'pos1': [1, 2, 1], 'pos2': [2, 2, 1]},
+ \]
+ call assert_equal(expect, savematches)
+
+ call clearmatches(winid)
+ call assert_equal([], getmatches(winid))
+
+ call setmatches(savematches, winid)
+ call assert_equal(expect, savematches)
+
wincmd w
bwipe!
call clearmatches()
@@ -250,6 +278,17 @@ func Test_matchaddpos_using_negative_priority()
set hlsearch&
endfunc
+func Test_matchaddpos_error()
+ call assert_fails("call matchaddpos('Error', 1)", 'E686:')
+ call assert_fails("call matchaddpos('Error', [1], 1, 1)", 'E798:')
+ call assert_fails("call matchaddpos('Error', [1], 1, 2)", 'E798:')
+ call assert_fails("call matchaddpos('Error', [1], 1, 0)", 'E799:')
+ call assert_fails("call matchaddpos('Error', [1], 1, 123, 1)", 'E715:')
+ call assert_fails("call matchaddpos('Error', [1], 1, 5, {'window':12345})", 'E957:')
+ " Why doesn't the following error have an error code E...?
+ call assert_fails("call matchaddpos('Error', [{}])", 'E5031:')
+endfunc
+
func OtherWindowCommon()
let lines =<< trim END
call setline(1, 'Hello Vim world')
@@ -275,6 +314,11 @@ func Test_matchdelete_other_window()
call delete('XscriptMatchCommon')
endfunc
+func Test_matchdelete_error()
+ call assert_fails("call matchdelete(0)", 'E802:')
+ call assert_fails("call matchdelete(1, -1)", 'E957:')
+endfunc
+
func Test_matchclear_other_window()
if !CanRunVimInTerminal()
throw 'Skipped: cannot make screendumps'
diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim
index 41f1710faf..400af33c58 100644
--- a/src/nvim/testdir/test_options.vim
+++ b/src/nvim/testdir/test_options.vim
@@ -42,6 +42,13 @@ function Test_wildchar()
set wildchar&
endfunction
+func Test_wildoptions()
+ set wildoptions=
+ set wildoptions+=tagfile
+ set wildoptions+=tagfile
+ call assert_equal('tagfile', &wildoptions)
+endfunc
+
function! Test_options()
let caught = 'ok'
try
diff --git a/src/nvim/testdir/test_startup.vim b/src/nvim/testdir/test_startup.vim
index f03c493275..9abaca5957 100644
--- a/src/nvim/testdir/test_startup.vim
+++ b/src/nvim/testdir/test_startup.vim
@@ -584,3 +584,12 @@ func Test_start_with_tabs()
" clean up
call StopVimInTerminal(buf)
endfunc
+
+func Test_v_argv()
+ let out = system(GetVimCommand() . ' -es -V1 -X arg1 --cmd "echo v:argv" --cmd q')
+ let list = split(out, "', '")
+ call assert_match('vim', list[0])
+ let idx = index(list, 'arg1')
+ call assert_true(idx > 2)
+ call assert_equal(['arg1', '--cmd', 'echo v:argv', '--cmd', 'q'']'], list[idx:])
+endfunc
diff --git a/test/functional/core/startup_spec.lua b/test/functional/core/startup_spec.lua
index cc10d36a10..3269fbc68d 100644
--- a/test/functional/core/startup_spec.lua
+++ b/test/functional/core/startup_spec.lua
@@ -277,6 +277,32 @@ describe('startup', function()
[4] = {bold = true, foreground = Screen.colors.Blue1},
}})
end)
+
+ it('fixed hang issue with --headless (#11386)', function()
+ local expected = ''
+ local period = 100
+ for i = 1, period - 1 do
+ expected = expected .. i .. '\r\n'
+ end
+ expected = expected .. period
+ eq(
+ expected,
+ -- FIXME(codehex): We should really set a timeout for the system function.
+ -- If this test fails, there will be a waiting input state.
+ funcs.system({nvim_prog, '-u', 'NONE', '-c',
+ 'for i in range(1, 100) | echo i | endfor | quit',
+ '--headless'
+ })
+ )
+ end)
+
+ it("get command line arguments from v:argv", function()
+ local out = funcs.system({ nvim_prog, '-u', 'NONE', '-i', 'NONE', '--headless',
+ '--cmd', nvim_set,
+ '-c', [[echo v:argv[-1:] len(v:argv) > 1]],
+ '+q' })
+ eq('[\'+q\'] 1', out)
+ end)
end)
describe('sysinit', function()
diff --git a/test/functional/ex_cmds/profile_spec.lua b/test/functional/ex_cmds/profile_spec.lua
index f185db192a..2b92f8d0de 100644
--- a/test/functional/ex_cmds/profile_spec.lua
+++ b/test/functional/ex_cmds/profile_spec.lua
@@ -6,6 +6,9 @@ local eval = helpers.eval
local command = helpers.command
local eq, neq = helpers.eq, helpers.neq
local tempfile = helpers.tmpname()
+local source = helpers.source
+local matches = helpers.matches
+local read_file = helpers.read_file
-- tmpname() also creates the file on POSIX systems. Remove it again.
-- We just need the name, ignoring any race conditions.
@@ -32,20 +35,67 @@ describe(':profile', function()
end
end)
- it('dump', function()
- eq(0, eval('v:profiling'))
- command('profile start ' .. tempfile)
- eq(1, eval('v:profiling'))
- assert_file_exists_not(tempfile)
- command('profile dump')
- assert_file_exists(tempfile)
+ describe('dump', function()
+ it('works', function()
+ eq(0, eval('v:profiling'))
+ command('profile start ' .. tempfile)
+ eq(1, eval('v:profiling'))
+ assert_file_exists_not(tempfile)
+ command('profile dump')
+ assert_file_exists(tempfile)
+ end)
+
+ it('not resetting the profile', function()
+ source([[
+ function! Test()
+ endfunction
+ ]])
+ command('profile start ' .. tempfile)
+ assert_file_exists_not(tempfile)
+ command('profile func Test')
+ command('call Test()')
+ command('profile dump')
+ assert_file_exists(tempfile)
+ local profile = read_file(tempfile)
+ matches('Called 1 time', profile)
+ command('call Test()')
+ command('profile dump')
+ assert_file_exists(tempfile)
+ profile = read_file(tempfile)
+ matches('Called 2 time', profile)
+ command('profile stop')
+ end)
end)
- it('stop', function()
- command('profile start ' .. tempfile)
- assert_file_exists_not(tempfile)
- command('profile stop')
- assert_file_exists(tempfile)
- eq(0, eval('v:profiling'))
+ describe('stop', function()
+ it('works', function()
+ command('profile start ' .. tempfile)
+ assert_file_exists_not(tempfile)
+ command('profile stop')
+ assert_file_exists(tempfile)
+ eq(0, eval('v:profiling'))
+ end)
+
+ it('resetting the profile', function()
+ source([[
+ function! Test()
+ endfunction
+ ]])
+ command('profile start ' .. tempfile)
+ assert_file_exists_not(tempfile)
+ command('profile func Test')
+ command('call Test()')
+ command('profile stop')
+ assert_file_exists(tempfile)
+ local profile = read_file(tempfile)
+ matches('Called 1 time', profile)
+ command('profile start ' .. tempfile)
+ command('profile func Test')
+ command('call Test()')
+ command('profile stop')
+ assert_file_exists(tempfile)
+ profile = read_file(tempfile)
+ matches('Called 1 time', profile)
+ end)
end)
end)
diff --git a/test/functional/lua/uri_spec.lua b/test/functional/lua/uri_spec.lua
index a3b8e685e1..f782769935 100644
--- a/test/functional/lua/uri_spec.lua
+++ b/test/functional/lua/uri_spec.lua
@@ -112,6 +112,29 @@ describe('URI methods', function()
eq('C:\\xy\\åäö\\ɧ\\汉语\\↥\\🤦\\🦄\\å\\بِيَّ.txt', exec_lua(test_case))
end)
end)
+
+ describe('decode non-file URI', function()
+ it('uri_to_fname returns non-file URI unchanged', function()
+ eq('jdt1.23+x-z://content/%5C/', exec_lua [[
+ return vim.uri_to_fname('jdt1.23+x-z://content/%5C/')
+ ]])
+ end)
+
+ it('uri_to_fname returns non-file upper-case scheme URI unchanged', function()
+ eq('JDT://content/%5C/', exec_lua [[
+ return vim.uri_to_fname('JDT://content/%5C/')
+ ]])
+ end)
+ end)
+
+ describe('decode URI without scheme', function()
+ it('fails because URI must have a scheme', function()
+ eq(false, exec_lua [[
+ return pcall(vim.uri_to_fname, 'not_an_uri.txt')
+ ]])
+ end)
+ end)
+
end)
describe('uri to bufnr', function()
diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua
index ca74d185cd..c68c05dffa 100644
--- a/test/functional/lua/vim_spec.lua
+++ b/test/functional/lua/vim_spec.lua
@@ -478,6 +478,17 @@ describe('lua stdlib', function()
return vim.tbl_islist(c) and vim.tbl_count(c) == 0
]]))
+ ok(exec_lua([[
+ local a = {x = {a = 1, b = 2}}
+ local b = {x = {a = 2, c = {y = 3}}}
+ local c = vim.tbl_extend("keep", a, b)
+
+ local count = 0
+ for _ in pairs(c) do count = count + 1 end
+
+ return c.x.a == 1 and c.x.b == 2 and c.x.c == nil and count == 1
+ ]]))
+
eq('Error executing lua: .../shared.lua: invalid "behavior": nil',
pcall_err(exec_lua, [[
return vim.tbl_extend()
@@ -497,6 +508,94 @@ describe('lua stdlib', function()
)
end)
+ it('vim.tbl_deep_extend', function()
+ ok(exec_lua([[
+ local a = {x = {a = 1, b = 2}}
+ local b = {x = {a = 2, c = {y = 3}}}
+ local c = vim.tbl_deep_extend("keep", a, b)
+
+ local count = 0
+ for _ in pairs(c) do count = count + 1 end
+
+ return c.x.a == 1 and c.x.b == 2 and c.x.c.y == 3 and count == 1
+ ]]))
+
+ ok(exec_lua([[
+ local a = {x = {a = 1, b = 2}}
+ local b = {x = {a = 2, c = {y = 3}}}
+ local c = vim.tbl_deep_extend("force", a, b)
+
+ local count = 0
+ for _ in pairs(c) do count = count + 1 end
+
+ return c.x.a == 2 and c.x.b == 2 and c.x.c.y == 3 and count == 1
+ ]]))
+
+ ok(exec_lua([[
+ local a = {x = {a = 1, b = 2}}
+ local b = {x = {a = 2, c = {y = 3}}}
+ local c = {x = {c = 4, d = {y = 4}}}
+ local d = vim.tbl_deep_extend("keep", a, b, c)
+
+ local count = 0
+ for _ in pairs(c) do count = count + 1 end
+
+ return d.x.a == 1 and d.x.b == 2 and d.x.c.y == 3 and d.x.d.y == 4 and count == 1
+ ]]))
+
+ ok(exec_lua([[
+ local a = {x = {a = 1, b = 2}}
+ local b = {x = {a = 2, c = {y = 3}}}
+ local c = {x = {c = 4, d = {y = 4}}}
+ local d = vim.tbl_deep_extend("force", a, b, c)
+
+ local count = 0
+ for _ in pairs(c) do count = count + 1 end
+
+ return d.x.a == 2 and d.x.b == 2 and d.x.c == 4 and d.x.d.y == 4 and count == 1
+ ]]))
+
+ ok(exec_lua([[
+ local a = vim.empty_dict()
+ local b = {}
+ local c = vim.tbl_deep_extend("keep", a, b)
+
+ local count = 0
+ for _ in pairs(c) do count = count + 1 end
+
+ return not vim.tbl_islist(c) and count == 0
+ ]]))
+
+ ok(exec_lua([[
+ local a = {}
+ local b = vim.empty_dict()
+ local c = vim.tbl_deep_extend("keep", a, b)
+
+ local count = 0
+ for _ in pairs(c) do count = count + 1 end
+
+ return vim.tbl_islist(c) and count == 0
+ ]]))
+
+ eq('Error executing lua: .../shared.lua: invalid "behavior": nil',
+ pcall_err(exec_lua, [[
+ return vim.tbl_deep_extend()
+ ]])
+ )
+
+ eq('Error executing lua: .../shared.lua: wrong number of arguments (given 1, expected at least 3)',
+ pcall_err(exec_lua, [[
+ return vim.tbl_deep_extend("keep")
+ ]])
+ )
+
+ eq('Error executing lua: .../shared.lua: wrong number of arguments (given 2, expected at least 3)',
+ pcall_err(exec_lua, [[
+ return vim.tbl_deep_extend("keep", {})
+ ]])
+ )
+ end)
+
it('vim.tbl_count', function()
eq(0, exec_lua [[ return vim.tbl_count({}) ]])
eq(0, exec_lua [[ return vim.tbl_count(vim.empty_dict()) ]])
@@ -769,10 +868,96 @@ describe('lua stdlib', function()
exec_lua [[
vim.api.nvim_set_var("testing", "hi")
vim.api.nvim_set_var("other", 123)
+ vim.api.nvim_set_var("to_delete", {hello="world"})
]]
+
eq('hi', funcs.luaeval "vim.g.testing")
eq(123, funcs.luaeval "vim.g.other")
eq(NIL, funcs.luaeval "vim.g.nonexistant")
+
+ eq({hello="world"}, funcs.luaeval "vim.g.to_delete")
+ exec_lua [[
+ vim.g.to_delete = nil
+ ]]
+ eq(NIL, funcs.luaeval "vim.g.to_delete")
+ end)
+
+ it('vim.b', function()
+ exec_lua [[
+ vim.api.nvim_buf_set_var(0, "testing", "hi")
+ vim.api.nvim_buf_set_var(0, "other", 123)
+ vim.api.nvim_buf_set_var(0, "to_delete", {hello="world"})
+ ]]
+
+ eq('hi', funcs.luaeval "vim.b.testing")
+ eq(123, funcs.luaeval "vim.b.other")
+ eq(NIL, funcs.luaeval "vim.b.nonexistant")
+
+ eq({hello="world"}, funcs.luaeval "vim.b.to_delete")
+ exec_lua [[
+ vim.b.to_delete = nil
+ ]]
+ eq(NIL, funcs.luaeval "vim.b.to_delete")
+
+ exec_lua [[
+ vim.cmd "vnew"
+ ]]
+
+ eq(NIL, funcs.luaeval "vim.b.testing")
+ eq(NIL, funcs.luaeval "vim.b.other")
+ eq(NIL, funcs.luaeval "vim.b.nonexistant")
+ end)
+
+ it('vim.w', function()
+ exec_lua [[
+ vim.api.nvim_win_set_var(0, "testing", "hi")
+ vim.api.nvim_win_set_var(0, "other", 123)
+ vim.api.nvim_win_set_var(0, "to_delete", {hello="world"})
+ ]]
+
+ eq('hi', funcs.luaeval "vim.w.testing")
+ eq(123, funcs.luaeval "vim.w.other")
+ eq(NIL, funcs.luaeval "vim.w.nonexistant")
+
+ eq({hello="world"}, funcs.luaeval "vim.w.to_delete")
+ exec_lua [[
+ vim.w.to_delete = nil
+ ]]
+ eq(NIL, funcs.luaeval "vim.w.to_delete")
+
+ exec_lua [[
+ vim.cmd "vnew"
+ ]]
+
+ eq(NIL, funcs.luaeval "vim.w.testing")
+ eq(NIL, funcs.luaeval "vim.w.other")
+ eq(NIL, funcs.luaeval "vim.w.nonexistant")
+ end)
+
+ it('vim.t', function()
+ exec_lua [[
+ vim.api.nvim_tabpage_set_var(0, "testing", "hi")
+ vim.api.nvim_tabpage_set_var(0, "other", 123)
+ vim.api.nvim_tabpage_set_var(0, "to_delete", {hello="world"})
+ ]]
+
+ eq('hi', funcs.luaeval "vim.t.testing")
+ eq(123, funcs.luaeval "vim.t.other")
+ eq(NIL, funcs.luaeval "vim.t.nonexistant")
+
+ eq({hello="world"}, funcs.luaeval "vim.t.to_delete")
+ exec_lua [[
+ vim.t.to_delete = nil
+ ]]
+ eq(NIL, funcs.luaeval "vim.t.to_delete")
+
+ exec_lua [[
+ vim.cmd "tabnew"
+ ]]
+
+ eq(NIL, funcs.luaeval "vim.t.testing")
+ eq(NIL, funcs.luaeval "vim.t.other")
+ eq(NIL, funcs.luaeval "vim.t.nonexistant")
end)
it('vim.env', function()
@@ -861,4 +1046,24 @@ describe('lua stdlib', function()
eq({}, exec_lua[[return {re1:match_line(0, 1, 1, 7)}]])
eq({0,3}, exec_lua[[return {re1:match_line(0, 1, 0, 7)}]])
end)
-end)
+
+ it('vim.defer_fn', function()
+ exec_lua [[
+ vim.g.test = 0
+ vim.defer_fn(function() vim.g.test = 1 end, 50)
+ ]]
+ eq(0, exec_lua[[return vim.g.test]])
+ exec_lua [[vim.cmd("sleep 1000m")]]
+ eq(1, exec_lua[[return vim.g.test]])
+ end)
+
+ it('vim.region', function()
+ helpers.insert(helpers.dedent( [[
+ text tααt tααt text
+ text tαxt txtα tex
+ text tαxt tαxt
+ ]]))
+ eq({5,15}, exec_lua[[ return vim.region(0,{1,5},{1,14},'v',true)[1] ]])
+ end)
+
+ end)
diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua
index 79f6ef9dd2..f41a5323a8 100644
--- a/test/functional/plugin/lsp_spec.lua
+++ b/test/functional/plugin/lsp_spec.lua
@@ -779,7 +779,7 @@ describe('LSP', function()
Fourth line of text
å å ɧ 汉语 ↥ 🤦 🦄]]))
end)
- it('applies apply simple edits', function()
+ it('applies simple edits', function()
local edits = {
make_edit(0, 0, 0, 0, {"123"});
make_edit(1, 0, 1, 1, {"2"});
@@ -818,10 +818,9 @@ describe('LSP', function()
'å å ɧ 汉语 ↥ 🤦 🦄';
}, buf_lines(1))
end)
- pending('applies non-ASCII characters edits', function()
- -- FIXME: We don't handle non-ASCII characters well in UTF-16
+ it('applies non-ASCII characters edits', function()
local edits = {
- make_edit(4, 0, 4, 14, {"a a h"});
+ make_edit(4, 3, 4, 4, {"ä"});
}
exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1)
eq({
@@ -829,40 +828,61 @@ describe('LSP', function()
'Second line of text';
'Third line of text';
'Fourth line of text';
- 'a a h';
+ 'å ä ɧ 汉语 ↥ 🤦 🦄';
}, buf_lines(1))
end)
+
+ describe('with LSP end line after what Vim considers to be the end line', function()
+ it('applies edits when the last linebreak is considered a new line', function()
+ local edits = {
+ make_edit(0, 0, 5, 0, {"All replaced"});
+ }
+ exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1)
+ eq({'All replaced'}, buf_lines(1))
+ end)
+ it('applies edits when the end line is 2 larger than vim\'s', function()
+ local edits = {
+ make_edit(0, 0, 6, 0, {"All replaced"});
+ }
+ exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1)
+ eq({'All replaced'}, buf_lines(1))
+ end)
+ it('applies edits with a column offset', function()
+ local edits = {
+ make_edit(0, 0, 5, 2, {"All replaced"});
+ }
+ exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1)
+ eq({'All replaced'}, buf_lines(1))
+ end)
+ end)
end)
describe('apply_text_document_edit', function()
local target_bufnr
+ local text_document_edit = function(editVersion)
+ return {
+ edits = {
+ make_edit(0, 0, 0, 3, "First ↥ 🤦 🦄")
+ },
+ textDocument = {
+ uri = "file://fake/uri";
+ version = editVersion
+ }
+ }
+ end
before_each(function()
target_bufnr = exec_lua [[
- local bufnr = vim.fn.bufadd("fake/uri")
- local lines = {"1st line of text", "2nd line of text"}
+ local bufnr = vim.uri_to_bufnr("file://fake/uri")
+ local lines = {"1st line of text", "2nd line of 语text"}
vim.api.nvim_buf_set_lines(bufnr, 0, 1, false, lines)
return bufnr
]]
end)
it('correctly goes ahead with the edit if all is normal', function()
- local text_document_edit = {
- edits = {
- make_edit(0, 0, 0, 3, "First")
- },
- textDocument = {
- uri = "file://fake/uri";
- version = 5
- }
- }
- exec_lua([[
- local args = {...}
- local target_bufnr = args[2]
- vim.lsp.util.buf_versions[target_bufnr] = 4
- vim.lsp.util.apply_text_document_edit(...)
- ]], text_document_edit, target_bufnr)
+ exec_lua('vim.lsp.util.apply_text_document_edit(...)', text_document_edit(5))
eq({
- 'First line of text';
- '2nd line of text';
+ 'First ↥ 🤦 🦄 line of text';
+ '2nd line of 语text';
}, buf_lines(target_bufnr))
end)
it('correctly goes ahead with the edit if the version is vim.NIL', function()
@@ -872,49 +892,56 @@ describe('LSP', function()
]]
eq(json.b, exec_lua("return vim.NIL"))
- local text_document_edit = {
- edits = {
- make_edit(0, 0, 0, 3, "First")
- },
- textDocument = {
- uri = "file://fake/uri";
- version = exec_lua("return vim.NIL")
- }
- }
- exec_lua([[
- local args = {...}
- local target_bufnr = args[2]
- vim.lsp.util.buf_versions[target_bufnr] = vim.NIL
- vim.lsp.util.apply_text_document_edit(...)
- ]], text_document_edit, target_bufnr)
+ exec_lua('vim.lsp.util.apply_text_document_edit(...)', text_document_edit(exec_lua("return vim.NIL")))
eq({
- 'First line of text';
- '2nd line of text';
+ 'First ↥ 🤦 🦄 line of text';
+ '2nd line of 语text';
}, buf_lines(target_bufnr))
end)
it('skips the edit if the version of the edit is behind the local buffer ', function()
- local text_document_edit = {
- edits = {
- make_edit(0, 0, 0, 3, "First")
- },
- textDocument = {
- uri = "file://fake/uri";
- version = 1
- }
+ local apply_edit_mocking_current_version = function(edit, versionedBuf)
+ exec_lua([[
+ local args = {...}
+ local versionedBuf = args[2]
+ vim.lsp.util.buf_versions[versionedBuf.bufnr] = versionedBuf.currentVersion
+ vim.lsp.util.apply_text_document_edit(...)
+ ]], edit, versionedBuf)
+ end
+
+ local baseText = {
+ '1st line of text';
+ '2nd line of 语text';
}
- exec_lua([[
- local args = {...}
- local target_bufnr = args[2]
- vim.lsp.util.buf_versions[target_bufnr] = 2
- vim.lsp.util.apply_text_document_edit(...)
- ]], text_document_edit, target_bufnr)
+
+ eq(baseText, buf_lines(target_bufnr))
+
+ -- Apply an edit for an old version, should skip
+ apply_edit_mocking_current_version(text_document_edit(2), {currentVersion=7; bufnr=target_bufnr})
+ eq(baseText, buf_lines(target_bufnr)) -- no change
+
+ -- Sanity check that next version to current does apply change
+ apply_edit_mocking_current_version(text_document_edit(8), {currentVersion=7; bufnr=target_bufnr})
eq({
- '1st line of text';
- '2nd line of text';
+ 'First ↥ 🤦 🦄 line of text';
+ '2nd line of 语text';
}, buf_lines(target_bufnr))
end)
end)
-
+ describe('workspace_apply_edit', function()
+ it('workspace/applyEdit returns ApplyWorkspaceEditResponse', function()
+ local expected = {
+ applied = true;
+ failureReason = nil;
+ }
+ eq(expected, exec_lua [[
+ local apply_edit = {
+ label = nil;
+ edit = {};
+ }
+ return vim.lsp.callbacks['workspace/applyEdit'](nil, nil, apply_edit)
+ ]])
+ end)
+ end)
describe('completion_list_to_complete_items', function()
-- Completion option precedence:
-- textEdit.newText > insertText > label
@@ -934,12 +961,12 @@ describe('LSP', function()
}
local completion_list_items = {items=completion_list}
local expected = {
- { abbr = 'foobar', dup = 1, empty = 1, icase = 1, info = ' ', kind = '', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label = 'foobar' } } } } },
- { abbr = 'foobar', dup = 1, empty = 1, icase = 1, info = ' ', kind = '', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foobar', textEdit={} } } } } },
- { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = '', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', insertText='foobar' } } } } },
- { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = '', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', insertText='foobar', textEdit={} } } } } },
- { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = '', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', insertText='foodar', textEdit={newText='foobar'} } } } } },
- { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = '', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', textEdit={newText='foobar'} } } } } },
+ { abbr = 'foobar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label = 'foobar' } } } } },
+ { abbr = 'foobar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foobar', textEdit={} } } } } },
+ { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', insertText='foobar' } } } } },
+ { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', insertText='foobar', textEdit={} } } } } },
+ { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', insertText='foodar', textEdit={newText='foobar'} } } } } },
+ { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', textEdit={newText='foobar'} } } } } },
}
eq(expected, exec_lua([[return vim.lsp.util.text_document_completion_list_to_complete_items(...)]], completion_list, prefix))
@@ -1239,4 +1266,28 @@ describe('LSP', function()
]])
end)
end)
+
+ describe('lsp.util._get_completion_item_kind_name', function()
+ describe('returns the name specified by protocol', function()
+ eq("Text", exec_lua("return vim.lsp.util._get_completion_item_kind_name(1)"))
+ eq("TypeParameter", exec_lua("return vim.lsp.util._get_completion_item_kind_name(25)"))
+ end)
+ describe('returns the name not specified by protocol', function()
+ eq("Unknown", exec_lua("return vim.lsp.util._get_completion_item_kind_name(nil)"))
+ eq("Unknown", exec_lua("return vim.lsp.util._get_completion_item_kind_name(vim.NIL)"))
+ eq("Unknown", exec_lua("return vim.lsp.util._get_completion_item_kind_name(1000)"))
+ end)
+ end)
+
+ describe('lsp.util._get_symbol_kind_name', function()
+ describe('returns the name specified by protocol', function()
+ eq("File", exec_lua("return vim.lsp.util._get_symbol_kind_name(1)"))
+ eq("TypeParameter", exec_lua("return vim.lsp.util._get_symbol_kind_name(26)"))
+ end)
+ describe('returns the name not specified by protocol', function()
+ eq("Unknown", exec_lua("return vim.lsp.util._get_symbol_kind_name(nil)"))
+ eq("Unknown", exec_lua("return vim.lsp.util._get_symbol_kind_name(vim.NIL)"))
+ eq("Unknown", exec_lua("return vim.lsp.util._get_symbol_kind_name(1000)"))
+ end)
+ end)
end)