aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.clang-format4
-rw-r--r--.github/workflows/issue-open-check.yml8
-rw-r--r--CMakeLists.txt3
-rw-r--r--cmake.config/config.h.in4
-rw-r--r--runtime/doc/api.txt5
-rw-r--r--runtime/doc/lsp.txt51
-rw-r--r--runtime/doc/news.txt7
-rw-r--r--runtime/ftplugin.vim8
-rw-r--r--runtime/lua/vim/filetype.lua2
-rw-r--r--runtime/lua/vim/filetype/detect.lua4
-rw-r--r--runtime/lua/vim/lsp.lua151
-rw-r--r--runtime/lua/vim/lsp/_dynamic.lua109
-rw-r--r--runtime/lua/vim/lsp/buf.lua13
-rw-r--r--runtime/lua/vim/lsp/handlers.lua28
-rw-r--r--runtime/lua/vim/lsp/protocol.lua13
-rw-r--r--runtime/lua/vim/lsp/types.lua28
-rw-r--r--scripts/gen_help_html.lua20
-rw-r--r--src/nvim/CMakeLists.txt3
-rw-r--r--src/nvim/api/extmark.c5
-rw-r--r--src/nvim/auevents.lua2
-rw-r--r--src/nvim/buffer.c11
-rw-r--r--src/nvim/decoration.c6
-rw-r--r--src/nvim/drawline.c4
-rw-r--r--src/nvim/eval/typval.c80
-rw-r--r--src/nvim/eval/typval.h74
-rw-r--r--src/nvim/eval/typval_defs.h21
-rw-r--r--src/nvim/eval/vars.c348
-rw-r--r--src/nvim/ex_cmds.c28
-rw-r--r--src/nvim/ex_cmds.lua2
-rw-r--r--src/nvim/ex_getln.c2
-rw-r--r--src/nvim/fileio.c63
-rw-r--r--src/nvim/main.c5
-rw-r--r--src/nvim/memfile.c19
-rw-r--r--src/nvim/memfile_defs.h68
-rw-r--r--src/nvim/memline.c4
-rw-r--r--src/nvim/memory.c1
-rw-r--r--src/nvim/ops.c9
-rw-r--r--src/nvim/ops.h9
-rw-r--r--src/nvim/os/stdpaths.c13
-rw-r--r--src/nvim/po/check.vim12
-rw-r--r--src/nvim/statusline.c39
-rw-r--r--src/nvim/testing.c1
-rw-r--r--test/functional/autocmd/textyankpost_spec.lua2
-rw-r--r--test/functional/options/defaults_spec.lua5
-rw-r--r--test/functional/plugin/lsp_spec.lua92
-rw-r--r--test/functional/provider/clipboard_spec.lua2
-rw-r--r--test/functional/provider/provider_spec.lua2
-rw-r--r--test/functional/ui/decorations_spec.lua208
-rw-r--r--test/functional/ui/highlight_spec.lua8
-rw-r--r--test/functional/ui/inccommand_user_spec.lua34
-rw-r--r--test/functional/ui/searchhl_spec.lua30
-rw-r--r--test/functional/ui/statuscolumn_spec.lua15
-rw-r--r--test/functional/vimscript/api_functions_spec.lua2
-rw-r--r--test/old/testdir/test_filetype.vim9
-rw-r--r--test/old/testdir/test_hlsearch.vim19
-rw-r--r--test/old/testdir/test_registers.vim5
56 files changed, 1141 insertions, 579 deletions
diff --git a/.clang-format b/.clang-format
index a31d753217..9f45ac32e2 100644
--- a/.clang-format
+++ b/.clang-format
@@ -14,7 +14,7 @@ PenaltyReturnTypeOnItsOwnLine: 200
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
-BinPackParameters: false
+BinPackParameters: true
BreakBeforeBinaryOperators: true
BreakBeforeTernaryOperators: true
ContinuationIndentWidth: 2
@@ -23,7 +23,7 @@ AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: No
AlwaysBreakTemplateDeclarations: No
AlignEscapedNewlines: DontAlign
-BinPackArguments: false
+BinPackArguments: true
BraceWrapping:
AfterClass: false
AfterControlStatement: false
diff --git a/.github/workflows/issue-open-check.yml b/.github/workflows/issue-open-check.yml
index 2471670dc6..aef1a90c38 100644
--- a/.github/workflows/issue-open-check.yml
+++ b/.github/workflows/issue-open-check.yml
@@ -17,14 +17,14 @@ jobs:
script: |
const title = context.payload.issue.title;
const titleSplit = title.split(/\s+/).map(e => e.toLowerCase());
- const keywords = ['api', 'treesitter', 'ui', 'lsp', 'doc'];
+ const keywords = ['api', 'treesitter', 'ui', 'lsp'];
var match = new Set();
- for(const keyword of keywords) {
- if(titleSplit.includes(keyword)) {
+ for (const keyword of keywords) {
+ if (titleSplit.includes(keyword)) {
match.add(keyword)
}
}
- if(match.size !== 0){
+ if (match.size !== 0) {
github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4e57d6905f..9b80f81bc8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -146,10 +146,7 @@ if(CMAKE_C_FLAGS_RELWITHDEBINFO MATCHES DNDEBUG)
string(REPLACE " " " " CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}") # Remove duplicate whitespace
endif()
-option(LOG_LIST_ACTIONS "Add list actions logging" OFF)
-
option(ENABLE_ASAN_UBSAN "Enable Clang address & undefined behavior sanitizer for nvim binary." OFF)
-option(LOG_DEBUG "Enable debug log messages even in a release build" OFF)
option(ENABLE_MSAN "Enable Clang memory sanitizer for nvim binary." OFF)
option(ENABLE_TSAN "Enable Clang thread sanitizer for nvim binary." OFF)
diff --git a/cmake.config/config.h.in b/cmake.config/config.h.in
index 87b39e8f6f..90916d55bd 100644
--- a/cmake.config/config.h.in
+++ b/cmake.config/config.h.in
@@ -45,10 +45,6 @@
#cmakedefine HAVE_DIRFD_AND_FLOCK
#cmakedefine HAVE_FORKPTY
-#ifndef UNIT_TESTING
-#cmakedefine LOG_LIST_ACTIONS
-#endif
-
#cmakedefine HAVE_BE64TOH
#cmakedefine ORDER_BIG_ENDIAN
#define ENDIAN_INCLUDE_FILE <@ENDIAN_INCLUDE_FILE@>
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt
index f399f1ed25..ed8858820e 100644
--- a/runtime/doc/api.txt
+++ b/runtime/doc/api.txt
@@ -2614,8 +2614,9 @@ nvim_buf_set_extmark({buffer}, {ns_id}, {line}, {col}, {*opts})
• virt_text_win_col : position the virtual text at a fixed
window column (starting from the first text column)
• virt_text_hide : hide the virtual text when the background
- text is selected or hidden due to horizontal scroll
- 'nowrap'
+ text is selected or hidden because of scrolling with
+ 'nowrap' or 'smoothscroll'. Currently only affects
+ "overlay" virt_text.
• hl_mode : control how highlights are combined with the
highlights of the text. Currently only affects virt_text
highlights, but might affect `hl_group` in later versions.
diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt
index 98a0801013..581cfd5348 100644
--- a/runtime/doc/lsp.txt
+++ b/runtime/doc/lsp.txt
@@ -619,19 +619,54 @@ to the callback in the "data" table. The token fields are documented in
Note: doing anything other than calling
|vim.lsp.semantic_tokens.highlight_token()| is considered experimental.
-Also the following |User| |autocommand|s are provided:
+
+LspRequest *LspRequest*
+
+For each request sent to an LSP server, this event is triggered for every
+change to the request's status. The status can be one of `pending`,
+`complete`, or `cancel` and is sent as the {type} on the "data" table passed
+to the callback function.
+
+It triggers when the initial request is sent ({type} == `pending`) and when
+the LSP server responds ({type} == `complete`). If a cancelation is requested
+using `client.cancel_request(request_id)`, then this event will trigger with
+{type} == `cancel`.
+
+When used from Lua, the client ID, request ID, and request are sent in the
+"data" table. See {requests} in |vim.lsp.client| for details on the {request}
+value. If the request type is `complete`, the request will be deleted from the
+client's pending requests table immediately after calling the event's
+callbacks. Example: >lua
+
+ vim.api.nvim_create_autocmd('LspRequest', {
+ callback = function(args)
+ local bufnr = args.buf
+ local client_id = args.data.client_id
+ local request_id = args.data.request_id
+ local request = args.data.request
+ if request.type == 'pending' then
+ -- do something with pending requests
+ track_pending(client_id, bufnr, request_id, request)
+ elseif request.type == 'cancel' then
+ -- do something with pending cancel requests
+ track_canceling(client_id, bufnr, request_id, request)
+ elseif request.type == 'complete' then
+ -- do something with finished requests. this pending
+ -- request entry is about to be removed since it is complete
+ track_finish(client_id, bufnr, request_id, request)
+ end
+ end,
+ })
+<
+
+Also the following |User| |autocommand| is provided:
LspProgressUpdate *LspProgressUpdate*
Upon receipt of a progress notification from the server. See
|vim.lsp.util.get_progress_messages()|.
-LspRequest *LspRequest*
- After a change to the active set of pending LSP requests. See {requests}
- in |vim.lsp.client|.
-
Example: >vim
autocmd User LspProgressUpdate redrawstatus
- autocmd User LspRequest redrawstatus
<
==============================================================================
@@ -764,7 +799,9 @@ client() *vim.lsp.client*
server. Entries are key-value pairs with the key being the request ID
while the value is a table with `type`, `bufnr`, and `method`
key-value pairs. `type` is either "pending" for an active request, or
- "cancel" for a cancel request.
+ "cancel" for a cancel request. It will be "complete" ephemerally while
+ executing |LspRequest| autocmds when replies are received from the
+ server.
• {config} (table): copy of the table that was passed by the user to
|vim.lsp.start_client()|.
• {server_capabilities} (table): Response from the server sent on
diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt
index 795ccc55de..72eb182fa5 100644
--- a/runtime/doc/news.txt
+++ b/runtime/doc/news.txt
@@ -31,11 +31,15 @@ The following changes may require adaptations in user config or plugins.
set keymodel=startsel,stopsel
<
+• |LspRequest| autocmd was promoted from a |User| autocmd to a first class
+ citizen.
+
==============================================================================
ADDED FEATURES *news-added*
The following new APIs or features were added.
+• Dynamic registration of LSP capabilities. An implication of this change is that checking a client's `server_capabilities` is no longer a sufficient indicator to see if a server supports a feature. Instead use `client.supports_method(<method>)`. It considers both the dynamic capabilities and static `server_capabilities`.
• |vim.iter()| provides a generic iterator interface for tables and Lua
iterators |luaref-in|.
@@ -79,6 +83,9 @@ The following changes to existing APIs or features add new behavior.
• The `workspace/didChangeWatchedFiles` LSP client capability is now enabled
by default.
+• |LspRequest| autocmd callbacks now contain additional information about the LSP
+ request status update that occurred.
+
==============================================================================
REMOVED FEATURES *news-removed*
diff --git a/runtime/ftplugin.vim b/runtime/ftplugin.vim
index feef949dba..d24c0a036e 100644
--- a/runtime/ftplugin.vim
+++ b/runtime/ftplugin.vim
@@ -28,9 +28,11 @@ augroup filetypeplugin
" When there is a dot it is used to separate filetype names. Thus for
" "aaa.bbb" load "aaa" and then "bbb".
for name in split(s, '\.')
- exe 'runtime! ftplugin/' . name . '.vim ftplugin/' . name . '_*.vim ftplugin/' . name . '/*.vim'
- " Load lua ftplugins
- exe printf('runtime! ftplugin/%s.lua ftplugin/%s_*.lua ftplugin/%s/*.lua', name, name, name)
+ " Load Lua ftplugins after Vim ftplugins _per directory_
+ " TODO(clason): use nvim__get_runtime when supports globs and modeline
+ exe printf('runtime! ftplugin/%s.vim ftplugin/%s.lua', name, name)
+ exe printf('runtime! ftplugin/%s_*.vim ftplugin/%s_*.lua', name, name)
+ exe printf('runtime! ftplugin/%s/*.vim ftplugin/%s/*.lua', name, name)
endfor
endif
endfunc
diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
index 1b04666161..6990de3391 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -241,6 +241,7 @@ local extension = {
copyright = function(path, bufnr)
return require('vim.filetype.detect').copyright(bufnr)
end,
+ corn = 'corn',
csh = function(path, bufnr)
return require('vim.filetype.detect').csh(path, bufnr)
end,
@@ -1547,6 +1548,7 @@ local filename = {
['man.config'] = 'manconf',
['maxima-init.mac'] = 'maxima',
['meson.build'] = 'meson',
+ ['meson.options'] = 'meson',
['meson_options.txt'] = 'meson',
['/etc/conf.modules'] = 'modconf',
['/etc/modules'] = 'modconf',
diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua
index 94106a3547..70e590ed10 100644
--- a/runtime/lua/vim/filetype/detect.lua
+++ b/runtime/lua/vim/filetype/detect.lua
@@ -771,14 +771,14 @@ end
function M.mod(path, bufnr)
if vim.g.filetype_mod then
return vim.g.filetype_mod
+ elseif matchregex(path, [[\c\<go\.mod$]]) then
+ return 'gomod'
elseif is_lprolog(bufnr) then
return 'lprolog'
elseif matchregex(nextnonblank(bufnr, 1), [[\%(\<MODULE\s\+\w\+\s*;\|^\s*(\*\)]]) then
return 'modula2'
elseif is_rapid(bufnr) then
return 'rapid'
- elseif matchregex(path, [[\c\<go\.mod$]]) then
- return 'gomod'
else
-- Nothing recognized, assume modsim3
return 'modsim3'
diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index 2e6ca7a0ac..d64ed0b5a3 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -50,6 +50,7 @@ lsp._request_name_to_capability = {
['textDocument/codeAction'] = { 'codeActionProvider' },
['textDocument/codeLens'] = { 'codeLensProvider' },
['codeLens/resolve'] = { 'codeLensProvider', 'resolveProvider' },
+ ['codeAction/resolve'] = { 'codeActionProvider', 'resolveProvider' },
['workspace/executeCommand'] = { 'executeCommandProvider' },
['workspace/symbol'] = { 'workspaceSymbolProvider' },
['textDocument/references'] = { 'referencesProvider' },
@@ -798,7 +799,9 @@ end
--- to the server. Entries are key-value pairs with the key
--- being the request ID while the value is a table with `type`,
--- `bufnr`, and `method` key-value pairs. `type` is either "pending"
---- for an active request, or "cancel" for a cancel request.
+--- for an active request, or "cancel" for a cancel request. It will
+--- be "complete" ephemerally while executing |LspRequest| autocmds
+--- when replies are received from the server.
---
--- - {config} (table): copy of the table that was passed by the user
--- to |vim.lsp.start_client()|.
@@ -886,6 +889,47 @@ function lsp.start(config, opts)
return client_id
end
+---@private
+-- Determines whether the given option can be set by `set_defaults`.
+local function is_empty_or_default(bufnr, option)
+ if vim.bo[bufnr][option] == '' then
+ return true
+ end
+
+ local info = vim.api.nvim_get_option_info2(option, { buf = bufnr })
+ local scriptinfo = vim.tbl_filter(function(e)
+ return e.sid == info.last_set_sid
+ end, vim.fn.getscriptinfo())
+
+ if #scriptinfo ~= 1 then
+ return false
+ end
+
+ return vim.startswith(scriptinfo[1].name, vim.fn.expand('$VIMRUNTIME'))
+end
+
+---@private
+---@param client lsp.Client
+function lsp._set_defaults(client, bufnr)
+ if
+ client.supports_method('textDocument/definition') and is_empty_or_default(bufnr, 'tagfunc')
+ then
+ vim.bo[bufnr].tagfunc = 'v:lua.vim.lsp.tagfunc'
+ end
+ if
+ client.supports_method('textDocument/completion') and is_empty_or_default(bufnr, 'omnifunc')
+ then
+ vim.bo[bufnr].omnifunc = 'v:lua.vim.lsp.omnifunc'
+ end
+ if
+ client.supports_method('textDocument/rangeFormatting')
+ and is_empty_or_default(bufnr, 'formatprg')
+ and is_empty_or_default(bufnr, 'formatexpr')
+ then
+ vim.bo[bufnr].formatexpr = 'v:lua.vim.lsp.formatexpr()'
+ end
+end
+
-- FIXME: DOC: Currently all methods on the `vim.lsp.client` object are
-- documented twice: Here, and on the methods themselves (e.g.
-- `client.request()`). This is a workaround for the vimdoc generator script
@@ -1091,43 +1135,6 @@ function lsp.start_client(config)
end
---@private
- -- Determines whether the given option can be set by `set_defaults`.
- local function is_empty_or_default(bufnr, option)
- if vim.bo[bufnr][option] == '' then
- return true
- end
-
- local info = vim.api.nvim_get_option_info2(option, { buf = bufnr })
- local scriptinfo = vim.tbl_filter(function(e)
- return e.sid == info.last_set_sid
- end, vim.fn.getscriptinfo())
-
- if #scriptinfo ~= 1 then
- return false
- end
-
- return vim.startswith(scriptinfo[1].name, vim.fn.expand('$VIMRUNTIME'))
- end
-
- ---@private
- local function set_defaults(client, bufnr)
- local capabilities = client.server_capabilities
- if capabilities.definitionProvider and is_empty_or_default(bufnr, 'tagfunc') then
- vim.bo[bufnr].tagfunc = 'v:lua.vim.lsp.tagfunc'
- end
- if capabilities.completionProvider and is_empty_or_default(bufnr, 'omnifunc') then
- vim.bo[bufnr].omnifunc = 'v:lua.vim.lsp.omnifunc'
- end
- if
- capabilities.documentRangeFormattingProvider
- and is_empty_or_default(bufnr, 'formatprg')
- and is_empty_or_default(bufnr, 'formatexpr')
- then
- vim.bo[bufnr].formatexpr = 'v:lua.vim.lsp.formatexpr()'
- end
- end
-
- ---@private
--- Reset defaults set by `set_defaults`.
--- Must only be called if the last client attached to a buffer exits.
local function unset_defaults(bufnr)
@@ -1228,7 +1235,9 @@ function lsp.start_client(config)
requests = {},
-- for $/progress report
messages = { name = name, messages = {}, progress = {}, status = {} },
+ dynamic_capabilities = require('vim.lsp._dynamic').new(client_id),
}
+ client.config.capabilities = config.capabilities or protocol.make_client_capabilities()
-- Store the uninitialized_clients for cleanup in case we exit before initialize finishes.
uninitialized_clients[client_id] = client
@@ -1291,7 +1300,7 @@ function lsp.start_client(config)
-- User provided initialization options.
initializationOptions = config.init_options,
-- The capabilities provided by the client (editor or tool)
- capabilities = config.capabilities or protocol.make_client_capabilities(),
+ capabilities = config.capabilities,
-- The initial trace setting. If omitted trace is disabled ("off").
-- trace = "off" | "messages" | "verbose";
trace = valid_traces[config.trace] or 'off',
@@ -1300,6 +1309,26 @@ function lsp.start_client(config)
-- TODO(ashkan) handle errors here.
pcall(config.before_init, initialize_params, config)
end
+
+ --- @param method string
+ --- @param opts? {bufnr?: number}
+ client.supports_method = function(method, opts)
+ opts = opts or {}
+ local required_capability = lsp._request_name_to_capability[method]
+ -- if we don't know about the method, assume that the client supports it.
+ if not required_capability then
+ return true
+ end
+ if vim.tbl_get(client.server_capabilities or {}, unpack(required_capability)) then
+ return true
+ else
+ if client.dynamic_capabilities:supports_registration(method) then
+ return client.dynamic_capabilities:supports(method, opts)
+ end
+ return false
+ end
+ end
+
local _ = log.trace() and log.trace(log_prefix, 'initialize_params', initialize_params)
rpc.request('initialize', initialize_params, function(init_err, result)
assert(not init_err, tostring(init_err))
@@ -1314,18 +1343,6 @@ function lsp.start_client(config)
client.server_capabilities =
assert(result.capabilities, "initialize result doesn't contain capabilities")
client.server_capabilities = protocol.resolve_capabilities(client.server_capabilities)
- client.supports_method = function(method)
- local required_capability = lsp._request_name_to_capability[method]
- -- if we don't know about the method, assume that the client supports it.
- if not required_capability then
- return true
- end
- if vim.tbl_get(client.server_capabilities, unpack(required_capability)) then
- return true
- else
- return false
- end
- end
if next(config.settings) then
client.notify('workspace/didChangeConfiguration', { settings = config.settings })
@@ -1393,13 +1410,24 @@ function lsp.start_client(config)
{ method = method, client_id = client_id, bufnr = bufnr, params = params }
)
end, function(request_id)
+ local request = client.requests[request_id]
+ request.type = 'complete'
+ nvim_exec_autocmds('LspRequest', {
+ buffer = bufnr,
+ modeline = false,
+ data = { client_id = client_id, request_id = request_id, request = request },
+ })
client.requests[request_id] = nil
- nvim_exec_autocmds('User', { pattern = 'LspRequest', modeline = false })
end)
if success and request_id then
- client.requests[request_id] = { type = 'pending', bufnr = bufnr, method = method }
- nvim_exec_autocmds('User', { pattern = 'LspRequest', modeline = false })
+ local request = { type = 'pending', bufnr = bufnr, method = method }
+ client.requests[request_id] = request
+ nvim_exec_autocmds('LspRequest', {
+ buffer = bufnr,
+ modeline = false,
+ data = { client_id = client_id, request_id = request_id, request = request },
+ })
end
return success, request_id
@@ -1471,7 +1499,11 @@ function lsp.start_client(config)
local request = client.requests[id]
if request and request.type == 'pending' then
request.type = 'cancel'
- nvim_exec_autocmds('User', { pattern = 'LspRequest', modeline = false })
+ nvim_exec_autocmds('LspRequest', {
+ buffer = request.bufnr,
+ modeline = false,
+ data = { client_id = client_id, request_id = id, request = request },
+ })
end
return rpc.notify('$/cancelRequest', { id = id })
end
@@ -1522,7 +1554,7 @@ function lsp.start_client(config)
function client._on_attach(bufnr)
text_document_did_open_handler(bufnr, client)
- set_defaults(client, bufnr)
+ lsp._set_defaults(client, bufnr)
nvim_exec_autocmds('LspAttach', {
buffer = bufnr,
@@ -1946,7 +1978,7 @@ function lsp.buf_request(bufnr, method, params, handler)
local supported_clients = {}
local method_supported = false
for_each_buffer_client(bufnr, function(client, client_id)
- if client.supports_method(method) then
+ if client.supports_method(method, { bufnr = bufnr }) then
method_supported = true
table.insert(supported_clients, client_id)
end
@@ -2002,7 +2034,7 @@ function lsp.buf_request_all(bufnr, method, params, callback)
local set_expected_result_count = once(function()
for_each_buffer_client(bufnr, function(client)
- if client.supports_method(method) then
+ if client.supports_method(method, { bufnr = bufnr }) then
expected_result_count = expected_result_count + 1
end
end)
@@ -2243,7 +2275,8 @@ end
---@param client_id (integer)
---@return boolean stopped true if client is stopped, false otherwise.
function lsp.client_is_stopped(client_id)
- return active_clients[client_id] == nil
+ assert(client_id, 'missing client_id param')
+ return active_clients[client_id] == nil and not uninitialized_clients[client_id]
end
--- Gets a map of client_id:client pairs for the given buffer, where each value
diff --git a/runtime/lua/vim/lsp/_dynamic.lua b/runtime/lua/vim/lsp/_dynamic.lua
new file mode 100644
index 0000000000..04040e8e28
--- /dev/null
+++ b/runtime/lua/vim/lsp/_dynamic.lua
@@ -0,0 +1,109 @@
+local wf = require('vim.lsp._watchfiles')
+
+--- @class lsp.DynamicCapabilities
+--- @field capabilities table<string, lsp.Registration[]>
+--- @field client_id number
+local M = {}
+
+--- @param client_id number
+function M.new(client_id)
+ return setmetatable({
+ capabilities = {},
+ client_id = client_id,
+ }, { __index = M })
+end
+
+function M:supports_registration(method)
+ local client = vim.lsp.get_client_by_id(self.client_id)
+ if not client then
+ return false
+ end
+ local capability = vim.tbl_get(client.config.capabilities, unpack(vim.split(method, '/')))
+ return type(capability) == 'table' and capability.dynamicRegistration
+end
+
+--- @param registrations lsp.Registration[]
+--- @private
+function M:register(registrations)
+ -- remove duplicates
+ self:unregister(registrations)
+ for _, reg in ipairs(registrations) do
+ local method = reg.method
+ if not self.capabilities[method] then
+ self.capabilities[method] = {}
+ end
+ table.insert(self.capabilities[method], reg)
+ end
+end
+
+--- @param unregisterations lsp.Unregistration[]
+--- @private
+function M:unregister(unregisterations)
+ for _, unreg in ipairs(unregisterations) do
+ local method = unreg.method
+ if not self.capabilities[method] then
+ return
+ end
+ local id = unreg.id
+ for i, reg in ipairs(self.capabilities[method]) do
+ if reg.id == id then
+ table.remove(self.capabilities[method], i)
+ break
+ end
+ end
+ end
+end
+
+--- @param method string
+--- @param opts? {bufnr?: number}
+--- @return lsp.Registration? (table|nil) the registration if found
+--- @private
+function M:get(method, opts)
+ opts = opts or {}
+ opts.bufnr = opts.bufnr or vim.api.nvim_get_current_buf()
+ for _, reg in ipairs(self.capabilities[method] or {}) do
+ if not reg.registerOptions then
+ return reg
+ end
+ local documentSelector = reg.registerOptions.documentSelector
+ if not documentSelector then
+ return reg
+ end
+ if M.match(opts.bufnr, documentSelector) then
+ return reg
+ end
+ end
+end
+
+--- @param method string
+--- @param opts? {bufnr?: number}
+--- @private
+function M:supports(method, opts)
+ return self:get(method, opts) ~= nil
+end
+
+--- @param bufnr number
+--- @param documentSelector lsp.DocumentSelector
+--- @private
+function M.match(bufnr, documentSelector)
+ local ft = vim.bo[bufnr].filetype
+ local uri = vim.uri_from_bufnr(bufnr)
+ local fname = vim.uri_to_fname(uri)
+ for _, filter in ipairs(documentSelector) do
+ local matches = true
+ if filter.language and ft ~= filter.language then
+ matches = false
+ end
+ if matches and filter.scheme and not vim.startswith(uri, filter.scheme .. ':') then
+ matches = false
+ end
+ if matches and filter.pattern and not wf._match(filter.pattern, fname) then
+ matches = false
+ end
+ if matches then
+ return true
+ end
+ end
+end
+
+return M
diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua
index a307dea673..bb3ca0e6d6 100644
--- a/runtime/lua/vim/lsp/buf.lua
+++ b/runtime/lua/vim/lsp/buf.lua
@@ -681,13 +681,16 @@ local function on_code_action_results(results, ctx, options)
-- command: string
-- arguments?: any[]
--
+ ---@type lsp.Client
local client = vim.lsp.get_client_by_id(action_tuple[1])
local action = action_tuple[2]
- if
- not action.edit
- and client
- and vim.tbl_get(client.server_capabilities, 'codeActionProvider', 'resolveProvider')
- then
+
+ local reg = client.dynamic_capabilities:get('textDocument/codeAction', { bufnr = ctx.bufnr })
+
+ local supports_resolve = vim.tbl_get(reg or {}, 'registerOptions', 'resolveProvider')
+ or client.supports_method('codeAction/resolve')
+
+ if not action.edit and client and supports_resolve then
client.request('codeAction/resolve', action, function(err, resolved_action)
if err then
vim.notify(err.code .. ': ' .. err.message, vim.log.levels.ERROR)
diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua
index 8e926c4644..5346160871 100644
--- a/runtime/lua/vim/lsp/handlers.lua
+++ b/runtime/lua/vim/lsp/handlers.lua
@@ -118,22 +118,30 @@ end
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#client_registerCapability
M['client/registerCapability'] = function(_, result, ctx)
- local log_unsupported = false
+ local client_id = ctx.client_id
+ ---@type lsp.Client
+ local client = vim.lsp.get_client_by_id(client_id)
+
+ client.dynamic_capabilities:register(result.registrations)
+ for bufnr, _ in ipairs(client.attached_buffers) do
+ vim.lsp._set_defaults(client, bufnr)
+ end
+
+ ---@type string[]
+ local unsupported = {}
for _, reg in ipairs(result.registrations) do
if reg.method == 'workspace/didChangeWatchedFiles' then
require('vim.lsp._watchfiles').register(reg, ctx)
- else
- log_unsupported = true
+ elseif not client.dynamic_capabilities:supports_registration(reg.method) then
+ unsupported[#unsupported + 1] = reg.method
end
end
- if log_unsupported then
- local client_id = ctx.client_id
+ if #unsupported > 0 then
local warning_tpl = 'The language server %s triggers a registerCapability '
- .. 'handler despite dynamicRegistration set to false. '
+ .. 'handler for %s despite dynamicRegistration set to false. '
.. 'Report upstream, this warning is harmless'
- local client = vim.lsp.get_client_by_id(client_id)
local client_name = client and client.name or string.format('id=%d', client_id)
- local warning = string.format(warning_tpl, client_name)
+ local warning = string.format(warning_tpl, client_name, table.concat(unsupported, ', '))
log.warn(warning)
end
return vim.NIL
@@ -141,6 +149,10 @@ end
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#client_unregisterCapability
M['client/unregisterCapability'] = function(_, result, ctx)
+ local client_id = ctx.client_id
+ local client = vim.lsp.get_client_by_id(client_id)
+ client.dynamic_capabilities:unregister(result.unregisterations)
+
for _, unreg in ipairs(result.unregisterations) do
if unreg.method == 'workspace/didChangeWatchedFiles' then
require('vim.lsp._watchfiles').unregister(unreg, ctx)
diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua
index a7919f12f5..a28ff407b7 100644
--- a/runtime/lua/vim/lsp/protocol.lua
+++ b/runtime/lua/vim/lsp/protocol.lua
@@ -697,7 +697,7 @@ function protocol.make_client_capabilities()
didSave = true,
},
codeAction = {
- dynamicRegistration = false,
+ dynamicRegistration = true,
codeActionLiteralSupport = {
codeActionKind = {
@@ -714,6 +714,12 @@ function protocol.make_client_capabilities()
properties = { 'edit' },
},
},
+ formatting = {
+ dynamicRegistration = true,
+ },
+ rangeFormatting = {
+ dynamicRegistration = true,
+ },
completion = {
dynamicRegistration = false,
completionItem = {
@@ -747,6 +753,7 @@ function protocol.make_client_capabilities()
},
definition = {
linkSupport = true,
+ dynamicRegistration = true,
},
implementation = {
linkSupport = true,
@@ -755,7 +762,7 @@ function protocol.make_client_capabilities()
linkSupport = true,
},
hover = {
- dynamicRegistration = false,
+ dynamicRegistration = true,
contentFormat = { protocol.MarkupKind.Markdown, protocol.MarkupKind.PlainText },
},
signatureHelp = {
@@ -790,7 +797,7 @@ function protocol.make_client_capabilities()
hierarchicalDocumentSymbolSupport = true,
},
rename = {
- dynamicRegistration = false,
+ dynamicRegistration = true,
prepareSupport = true,
},
publishDiagnostics = {
diff --git a/runtime/lua/vim/lsp/types.lua b/runtime/lua/vim/lsp/types.lua
index 779f313aa7..e77e1fb63a 100644
--- a/runtime/lua/vim/lsp/types.lua
+++ b/runtime/lua/vim/lsp/types.lua
@@ -35,3 +35,31 @@
---@field source string
---@field tags? lsp.DiagnosticTag[]
---@field relatedInformation DiagnosticRelatedInformation[]
+
+--- @class lsp.DocumentFilter
+--- @field language? string
+--- @field scheme? string
+--- @field pattern? string
+
+--- @alias lsp.DocumentSelector lsp.DocumentFilter[]
+
+--- @alias lsp.RegisterOptions any | lsp.StaticRegistrationOptions | lsp.TextDocumentRegistrationOptions
+
+--- @class lsp.Registration
+--- @field id string
+--- @field method string
+--- @field registerOptions? lsp.RegisterOptions
+
+--- @alias lsp.RegistrationParams {registrations: lsp.Registration[]}
+
+--- @class lsp.StaticRegistrationOptions
+--- @field id? string
+
+--- @class lsp.TextDocumentRegistrationOptions
+--- @field documentSelector? lsp.DocumentSelector
+
+--- @class lsp.Unregistration
+--- @field id string
+--- @field method string
+
+--- @alias lsp.UnregistrationParams {unregisterations: lsp.Unregistration[]}
diff --git a/scripts/gen_help_html.lua b/scripts/gen_help_html.lua
index 1bddd3aa8b..96289c45ec 100644
--- a/scripts/gen_help_html.lua
+++ b/scripts/gen_help_html.lua
@@ -696,6 +696,11 @@ local function gen_one(fname, to_fname, old, commit)
<link href="/css/main.css" rel="stylesheet">
<link href="help.css" rel="stylesheet">
<link href="/highlight/styles/neovim.min.css" rel="stylesheet">
+
+ <!-- algolia docsearch https://docsearch.algolia.com/docs/docsearch-v3/ -->
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@docsearch/css@3" />
+ <link rel="preconnect" href="https://X185E15FPG-dsn.algolia.net" crossorigin />
+
<script src="/highlight/highlight.min.js"></script>
<script>hljs.highlightAll();</script>
<title>%s - Neovim docs</title>
@@ -766,12 +771,13 @@ local function gen_one(fname, to_fname, old, commit)
main = ([[
<header class="container">
<nav class="navbar navbar-expand-lg">
- <div>
+ <div class="container-fluid">
<a href="/" class="navbar-brand" aria-label="logo">
<!--TODO: use <img src="….svg"> here instead. Need one that has green lettering instead of gray. -->
%s
<!--<img src="https://neovim.io/logos/neovim-logo.svg" width="173" height="50" alt="Neovim" />-->
</a>
+ <div id="docsearch"></div> <!-- algolia docsearch https://docsearch.algolia.com/docs/docsearch-v3/ -->
</div>
</nav>
</header>
@@ -825,6 +831,18 @@ local function gen_one(fname, to_fname, old, commit)
parse_errors: %d %s | <span title="%s">noise_lines: %d</span>
</div>
<div>
+
+ <!-- algolia docsearch https://docsearch.algolia.com/docs/docsearch-v3/ -->
+ <script src="https://cdn.jsdelivr.net/npm/@docsearch/js@3"></script>
+ <script type="module">
+ docsearch({
+ container: '#docsearch',
+ appId: 'X185E15FPG',
+ apiKey: 'b5e6b2f9c636b2b471303205e59832ed',
+ indexName: 'nvim',
+ });
+ </script>
+
</footer>
]]):format(
os.date('%Y-%m-%d %H:%M'), commit, commit:sub(1, 7), #stats.parse_errors, bug_link,
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt
index 47f32abc07..222b283a5d 100644
--- a/src/nvim/CMakeLists.txt
+++ b/src/nvim/CMakeLists.txt
@@ -394,9 +394,6 @@ endif()
if($ENV{CI})
# Don't debug log on CI, it gets too verbose in the main build log.
# TODO(bfredl): debug log level also exposes some errors with EXITFREE in ASAN build.
- set(LOG_DEBUG FALSE)
-elseif(LOG_DEBUG)
- target_compile_definitions(nvim PRIVATE NVIM_LOG_DEBUG)
else()
# Minimize logging for release-type builds.
target_compile_definitions(nvim PRIVATE $<$<CONFIG:Debug>:NVIM_LOG_DEBUG>)
diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c
index 299413e510..aca290494b 100644
--- a/src/nvim/api/extmark.c
+++ b/src/nvim/api/extmark.c
@@ -483,8 +483,9 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
/// window column (starting from the first
/// text column)
/// - virt_text_hide : hide the virtual text when the background
-/// text is selected or hidden due to
-/// horizontal scroll 'nowrap'
+/// text is selected or hidden because of
+/// scrolling with 'nowrap' or 'smoothscroll'.
+/// Currently only affects "overlay" virt_text.
/// - hl_mode : control how highlights are combined with the
/// highlights of the text. Currently only affects
/// virt_text highlights, but might affect `hl_group`
diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua
index aef08be820..048b8d6631 100644
--- a/src/nvim/auevents.lua
+++ b/src/nvim/auevents.lua
@@ -72,6 +72,7 @@ return {
'InsertLeavePre', -- just before leaving Insert mode
'LspAttach', -- after an LSP client attaches to a buffer
'LspDetach', -- after an LSP client detaches from a buffer
+ 'LspRequest', -- after an LSP request is started, canceled, or completed
'LspTokenUpdate', -- after a visible LSP token is updated
'MenuPopup', -- just before popup menu is displayed
'ModeChanged', -- after changing the mode
@@ -152,6 +153,7 @@ return {
DiagnosticChanged=true,
LspAttach=true,
LspDetach=true,
+ LspRequest=true,
LspTokenUpdate=true,
RecordingEnter=true,
RecordingLeave=true,
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index dab07487cd..bc52ab0771 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -248,6 +248,11 @@ int open_buffer(int read_stdin, exarg_T *eap, int flags_arg)
return FAIL;
}
+ // Do not sync this buffer yet, may first want to read the file.
+ if (curbuf->b_ml.ml_mfp != NULL) {
+ curbuf->b_ml.ml_mfp->mf_dirty = MF_DIRTY_YES_NOSYNC;
+ }
+
// The autocommands in readfile() may change the buffer, but only AFTER
// reading the file.
set_bufref(&old_curbuf, curbuf);
@@ -316,6 +321,12 @@ int open_buffer(int read_stdin, exarg_T *eap, int flags_arg)
}
}
+ // Can now sync this buffer in ml_sync_all().
+ if (curbuf->b_ml.ml_mfp != NULL
+ && curbuf->b_ml.ml_mfp->mf_dirty == MF_DIRTY_YES_NOSYNC) {
+ curbuf->b_ml.ml_mfp->mf_dirty = MF_DIRTY_YES;
+ }
+
// if first time loading this buffer, init b_chartab[]
if (curbuf->b_flags & BF_NEVERLOADED) {
(void)buf_init_chartab(curbuf, false);
diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c
index 81e1cb617c..2027848ccf 100644
--- a/src/nvim/decoration.c
+++ b/src/nvim/decoration.c
@@ -433,7 +433,9 @@ void decor_redraw_signs(buf_T *buf, int row, int *num_signs, SignTextAttrs sattr
if (sattrs[j - 1].priority >= decor->priority) {
break;
}
- sattrs[j] = sattrs[j - 1];
+ if (j < SIGN_SHOW_MAX) {
+ sattrs[j] = sattrs[j - 1];
+ }
}
if (j < SIGN_SHOW_MAX) {
sattrs[j] = (SignTextAttrs) {
@@ -603,7 +605,7 @@ int decor_virt_lines(win_T *wp, linenr_T lnum, VirtLines *lines, TriState has_fo
mtkey_t mark = marktree_itr_current(itr);
if (mark.pos.row < 0 || mark.pos.row >= end_row) {
break;
- } else if (marktree_decor_level(mark) < kDecorLevelVirtLine) {
+ } else if (mt_end(mark) || marktree_decor_level(mark) < kDecorLevelVirtLine) {
goto next_mark;
}
bool above = mark.pos.row > (lnum - 2);
diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c
index 557e58ff27..ced7e46287 100644
--- a/src/nvim/drawline.c
+++ b/src/nvim/drawline.c
@@ -1715,8 +1715,8 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
sign_idx = 0;
wlv.draw_state = WL_LINE;
if (has_decor && wlv.row == startrow + wlv.filler_lines) {
- // hide virt_text on text hidden by 'nowrap'
- decor_redraw_col(wp, (colnr_T)(ptr - line), wlv.off, true, &decor_state);
+ // hide virt_text on text hidden by 'nowrap' or 'smoothscroll'
+ decor_redraw_col(wp, (colnr_T)(ptr - line) - 1, wlv.off, true, &decor_state);
}
win_line_continue(&wlv); // use wlv.saved_ values
}
diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c
index cb8f8ce44d..42e9dc8f03 100644
--- a/src/nvim/eval/typval.c
+++ b/src/nvim/eval/typval.c
@@ -82,70 +82,6 @@ bool tv_in_free_unref_items = false;
const char *const tv_empty_string = "";
//{{{1 Lists
-//{{{2 List log
-#ifdef LOG_LIST_ACTIONS
-ListLog *list_log_first = NULL;
-ListLog *list_log_last = NULL;
-
-/// Write list log to the given file
-///
-/// @param[in] fname File to write log to. Will be appended to if already
-/// present.
-void list_write_log(const char *const fname)
- FUNC_ATTR_NONNULL_ALL
-{
- FileDescriptor fp;
- const int fo_ret = file_open(&fp, fname, kFileCreate|kFileAppend, 0600);
- if (fo_ret != 0) {
- semsg(_("E5142: Failed to open file %s: %s"), fname, os_strerror(fo_ret));
- return;
- }
- for (ListLog *chunk = list_log_first; chunk != NULL;) {
- for (size_t i = 0; i < chunk->size; i++) {
- char buf[10 + 1 + ((16 + 3) * 3) + (8 + 2) + 2];
- // act : hex " c:" len "[]" "\n\0"
- const ListLogEntry entry = chunk->entries[i];
- const size_t snp_len = (size_t)snprintf(buf, sizeof(buf),
- "%-10.10s: l:%016" PRIxPTR "[%08d] 1:%016" PRIxPTR " 2:%016" PRIxPTR
- "\n",
- entry.action, entry.l, entry.len, entry.li1,
- entry.li2);
- assert(snp_len + 1 == sizeof(buf));
- const ptrdiff_t fw_ret = file_write(&fp, buf, snp_len);
- if (fw_ret != (ptrdiff_t)snp_len) {
- assert(fw_ret < 0);
- if (i) {
- memmove(chunk->entries, chunk->entries + i,
- sizeof(chunk->entries[0]) * (chunk->size - i));
- chunk->size -= i;
- }
- semsg(_("E5143: Failed to write to file %s: %s"),
- fname, os_strerror((int)fw_ret));
- return;
- }
- }
- list_log_first = chunk->next;
- xfree(chunk);
- chunk = list_log_first;
- }
- const int fc_ret = file_close(&fp, true);
- if (fc_ret != 0) {
- semsg(_("E5144: Failed to close file %s: %s"), fname, os_strerror(fc_ret));
- }
-}
-
-# ifdef EXITFREE
-/// Free list log
-void list_free_log(void)
-{
- for (ListLog *chunk = list_log_first; chunk != NULL;) {
- list_log_first = chunk->next;
- xfree(chunk);
- chunk = list_log_first;
- }
-}
-# endif
-#endif
//{{{2 List item
/// Allocate a list item
@@ -252,7 +188,6 @@ list_T *tv_list_alloc(const ptrdiff_t len)
list->lv_used_prev = NULL;
list->lv_used_next = gc_first_list;
gc_first_list = list;
- list_log(list, NULL, (void *)(uintptr_t)len, "alloc");
list->lua_table_ref = LUA_NOREF;
return list;
}
@@ -283,8 +218,6 @@ void tv_list_init_static10(staticList10_T *const sl)
li->li_prev = li - 1;
li->li_next = li + 1;
}
- list_log((const list_T *)sl, &sl->sl_items[0], &sl->sl_items[SL_SIZE - 1],
- "s10init");
#undef SL_SIZE
}
@@ -296,7 +229,6 @@ void tv_list_init_static(list_T *const l)
{
CLEAR_POINTER(l);
l->lv_refcount = DO_NOT_FREE_CNT;
- list_log(l, NULL, NULL, "sinit");
}
/// Free items contained in a list
@@ -305,7 +237,6 @@ void tv_list_init_static(list_T *const l)
void tv_list_free_contents(list_T *const l)
FUNC_ATTR_NONNULL_ALL
{
- list_log(l, NULL, NULL, "freecont");
for (listitem_T *item = l->lv_first; item != NULL; item = l->lv_first) {
// Remove the item before deleting it.
l->lv_first = item->li_next;
@@ -335,7 +266,6 @@ void tv_list_free_list(list_T *const l)
if (l->lv_used_next != NULL) {
l->lv_used_next->lv_used_prev = l->lv_used_prev;
}
- list_log(l, NULL, NULL, "freelist");
NLUA_CLEAR_REF(l->lua_table_ref);
xfree(l);
@@ -382,7 +312,6 @@ void tv_list_unref(list_T *const l)
void tv_list_drop_items(list_T *const l, listitem_T *const item, listitem_T *const item2)
FUNC_ATTR_NONNULL_ALL
{
- list_log(l, item, item2, "drop");
// Notify watchers.
for (listitem_T *ip = item; ip != item2->li_next; ip = ip->li_next) {
l->lv_len--;
@@ -400,14 +329,12 @@ void tv_list_drop_items(list_T *const l, listitem_T *const item, listitem_T *con
item->li_prev->li_next = item2->li_next;
}
l->lv_idx_item = NULL;
- list_log(l, l->lv_first, l->lv_last, "afterdrop");
}
/// Like tv_list_drop_items, but also frees all removed items
void tv_list_remove_items(list_T *const l, listitem_T *const item, listitem_T *const item2)
FUNC_ATTR_NONNULL_ALL
{
- list_log(l, item, item2, "remove");
tv_list_drop_items(l, item, item2);
for (listitem_T *li = item;;) {
tv_clear(TV_LIST_ITEM_TV(li));
@@ -431,7 +358,6 @@ void tv_list_move_items(list_T *const l, listitem_T *const item, listitem_T *con
list_T *const tgt_l, const int cnt)
FUNC_ATTR_NONNULL_ALL
{
- list_log(l, item, item2, "move");
tv_list_drop_items(l, item, item2);
item->li_prev = tgt_l->lv_last;
item2->li_next = NULL;
@@ -442,7 +368,6 @@ void tv_list_move_items(list_T *const l, listitem_T *const item, listitem_T *con
}
tgt_l->lv_last = item2;
tgt_l->lv_len += cnt;
- list_log(tgt_l, tgt_l->lv_first, tgt_l->lv_last, "movetgt");
}
/// Insert list item
@@ -470,7 +395,6 @@ void tv_list_insert(list_T *const l, listitem_T *const ni, listitem_T *const ite
}
item->li_prev = ni;
l->lv_len++;
- list_log(l, ni, item, "insert");
}
}
@@ -496,7 +420,6 @@ void tv_list_insert_tv(list_T *const l, typval_T *const tv, listitem_T *const it
void tv_list_append(list_T *const l, listitem_T *const item)
FUNC_ATTR_NONNULL_ALL
{
- list_log(l, item, NULL, "append");
if (l->lv_last == NULL) {
// empty list
l->lv_first = item;
@@ -1416,7 +1339,6 @@ void tv_list_reverse(list_T *const l)
if (tv_list_len(l) <= 1) {
return;
}
- list_log(l, NULL, NULL, "reverse");
#define SWAP(a, b) \
do { \
tmp = (a); \
@@ -1454,7 +1376,6 @@ void tv_list_item_sort(list_T *const l, ListSortItem *const ptrs,
if (len <= 1) {
return;
}
- list_log(l, NULL, NULL, "sort");
int i = 0;
TV_LIST_ITER(l, li, {
ptrs[i].item = li;
@@ -1543,7 +1464,6 @@ listitem_T *tv_list_find(list_T *const l, int n)
// Cache the used index.
l->lv_idx = idx;
l->lv_idx_item = item;
- list_log(l, l->lv_idx_item, (void *)(uintptr_t)l->lv_idx, "find");
return item;
}
diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h
index 767fd706b3..e7b2499346 100644
--- a/src/nvim/eval/typval.h
+++ b/src/nvim/eval/typval.h
@@ -18,74 +18,6 @@
#include "nvim/message.h"
#include "nvim/types.h"
-#ifdef LOG_LIST_ACTIONS
-# include "nvim/memory.h"
-
-extern ListLog *list_log_first; ///< First list log chunk, NULL if missing
-extern ListLog *list_log_last; ///< Last list log chunk
-
-static inline ListLog *list_log_alloc(const size_t size)
- REAL_FATTR_ALWAYS_INLINE REAL_FATTR_WARN_UNUSED_RESULT;
-
-/// Allocate a new log chunk and update globals
-///
-/// @param[in] size Number of entries in a new chunk.
-///
-/// @return [allocated] Newly allocated chunk.
-static inline ListLog *list_log_new(const size_t size)
-{
- ListLog *ret = xmalloc(offsetof(ListLog, entries)
- + size * sizeof(ret->entries[0]));
- ret->size = 0;
- ret->capacity = size;
- ret->next = NULL;
- if (list_log_first == NULL) {
- list_log_first = ret;
- } else {
- list_log_last->next = ret;
- }
- list_log_last = ret;
- return ret;
-}
-
-static inline void list_log(const list_T *const l, const listitem_T *const li1,
- const listitem_T *const li2, const char *const action)
- REAL_FATTR_ALWAYS_INLINE;
-
-/// Add new entry to log
-///
-/// If last chunk was filled it uses twice as much memory to allocate the next
-/// chunk.
-///
-/// @param[in] l List to which entry belongs.
-/// @param[in] li1 List item 1.
-/// @param[in] li2 List item 2, often used for integers and not list items.
-/// @param[in] action Logged action.
-static inline void list_log(const list_T *const l, const listitem_T *const li1,
- const listitem_T *const li2, const char *const action)
-{
- ListLog *tgt;
- if (list_log_first == NULL) {
- tgt = list_log_new(128);
- } else if (list_log_last->size == list_log_last->capacity) {
- tgt = list_log_new(list_log_last->capacity * 2);
- } else {
- tgt = list_log_last;
- }
- tgt->entries[tgt->size++] = (ListLogEntry) {
- .l = (uintptr_t)l,
- .li1 = (uintptr_t)li1,
- .li2 = (uintptr_t)li2,
- .len = (l == NULL ? 0 : l->lv_len),
- .action = action,
- };
-}
-#else
-# define list_log(...)
-# define list_write_log(...)
-# define list_free_log()
-#endif
-
// In a hashtab item "hi_key" points to "di_key" in a dictitem.
// This avoids adding a pointer to the hashtab item.
@@ -174,7 +106,6 @@ static inline int tv_list_len(const list_T *l)
/// @param[in] l List to check.
static inline int tv_list_len(const list_T *const l)
{
- list_log(l, NULL, NULL, "len");
if (l == NULL) {
return 0;
}
@@ -258,10 +189,8 @@ static inline listitem_T *tv_list_first(const list_T *l)
static inline listitem_T *tv_list_first(const list_T *const l)
{
if (l == NULL) {
- list_log(l, NULL, NULL, "first");
return NULL;
}
- list_log(l, l->lv_first, NULL, "first");
return l->lv_first;
}
@@ -276,10 +205,8 @@ static inline listitem_T *tv_list_last(const list_T *l)
static inline listitem_T *tv_list_last(const list_T *const l)
{
if (l == NULL) {
- list_log(l, NULL, NULL, "last");
return NULL;
}
- list_log(l, l->lv_last, NULL, "last");
return l->lv_last;
}
@@ -416,7 +343,6 @@ extern bool tv_in_free_unref_items;
#define _TV_LIST_ITER_MOD(modifier, l, li, code) \
do { \
modifier list_T *const l_ = (l); \
- list_log(l_, NULL, NULL, "iter" #modifier); \
if (l_ != NULL) { \
for (modifier listitem_T *li = l_->lv_first; \
li != NULL; li = li->li_next) { \
diff --git a/src/nvim/eval/typval_defs.h b/src/nvim/eval/typval_defs.h
index 3e49417f6f..767603ac0e 100644
--- a/src/nvim/eval/typval_defs.h
+++ b/src/nvim/eval/typval_defs.h
@@ -375,25 +375,4 @@ typedef struct {
typedef int (*ListSorter)(const void *, const void *);
-#ifdef LOG_LIST_ACTIONS
-/// List actions log entry
-typedef struct {
- uintptr_t l; ///< List log entry belongs to.
- uintptr_t li1; ///< First list item log entry belongs to, if applicable.
- uintptr_t li2; ///< Second list item log entry belongs to, if applicable.
- int len; ///< List length when log entry was created.
- const char *action; ///< Logged action.
-} ListLogEntry;
-
-typedef struct list_log ListLog;
-
-/// List actions log
-struct list_log {
- ListLog *next; ///< Next chunk or NULL.
- size_t capacity; ///< Number of entries in current chunk.
- size_t size; ///< Current chunk size.
- ListLogEntry entries[]; ///< Actual log entries.
-};
-#endif
-
#endif // NVIM_EVAL_TYPVAL_DEFS_H
diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c
index 64177d13e8..21b25b64f4 100644
--- a/src/nvim/eval/vars.c
+++ b/src/nvim/eval/vars.c
@@ -701,6 +701,189 @@ static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first)
return arg;
}
+/// Set an environment variable, part of ex_let_one().
+static char *ex_let_env(char *arg, typval_T *const tv, const bool is_const,
+ const char *const endchars, const char *const op)
+ FUNC_ATTR_NONNULL_ARG(1, 2) FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ if (is_const) {
+ emsg(_("E996: Cannot lock an environment variable"));
+ return NULL;
+ }
+
+ // Find the end of the name.
+ char *arg_end = NULL;
+ arg++;
+ char *name = arg;
+ int len = get_env_len((const char **)&arg);
+ if (len == 0) {
+ semsg(_(e_invarg2), name - 1);
+ } else {
+ if (op != NULL && vim_strchr("+-*/%", (uint8_t)(*op)) != NULL) {
+ semsg(_(e_letwrong), op);
+ } else if (endchars != NULL
+ && vim_strchr(endchars, (uint8_t)(*skipwhite(arg))) == NULL) {
+ emsg(_(e_letunexp));
+ } else if (!check_secure()) {
+ char *tofree = NULL;
+ const char c1 = name[len];
+ name[len] = NUL;
+ const char *p = tv_get_string_chk(tv);
+ if (p != NULL && op != NULL && *op == '.') {
+ char *s = vim_getenv(name);
+ if (s != NULL) {
+ tofree = concat_str(s, p);
+ p = tofree;
+ xfree(s);
+ }
+ }
+ if (p != NULL) {
+ vim_setenv_ext(name, p);
+ arg_end = arg;
+ }
+ name[len] = c1;
+ xfree(tofree);
+ }
+ }
+ return arg_end;
+}
+
+/// Set an option, part of ex_let_one().
+static char *ex_let_option(char *arg, typval_T *const tv, const bool is_const,
+ const char *const endchars, const char *const op)
+ FUNC_ATTR_NONNULL_ARG(1, 2) FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ if (is_const) {
+ emsg(_("E996: Cannot lock an option"));
+ return NULL;
+ }
+
+ // Find the end of the name.
+ char *arg_end = NULL;
+ int scope;
+ char *const p = (char *)find_option_end((const char **)&arg, &scope);
+ if (p == NULL
+ || (endchars != NULL
+ && vim_strchr(endchars, (uint8_t)(*skipwhite(p))) == NULL)) {
+ emsg(_(e_letunexp));
+ } else {
+ varnumber_T n = 0;
+ getoption_T opt_type;
+ long numval;
+ char *stringval = NULL;
+ const char *s = NULL;
+ bool failed = false;
+ uint32_t opt_p_flags;
+ char *tofree = NULL;
+
+ const char c1 = *p;
+ *p = NUL;
+
+ opt_type = get_option_value(arg, &numval, &stringval, &opt_p_flags, scope);
+ if (opt_type == gov_bool
+ || opt_type == gov_number
+ || opt_type == gov_hidden_bool
+ || opt_type == gov_hidden_number) {
+ // number, possibly hidden
+ n = (long)tv_get_number(tv);
+ }
+
+ if ((opt_p_flags & P_FUNC) && tv_is_func(*tv)) {
+ // If the option can be set to a function reference or a lambda
+ // and the passed value is a function reference, then convert it to
+ // the name (string) of the function reference.
+ s = tofree = encode_tv2string(tv, NULL);
+ } else if (tv->v_type != VAR_BOOL && tv->v_type != VAR_SPECIAL) {
+ // Avoid setting a string option to the text "v:false" or similar.
+ s = tv_get_string_chk(tv);
+ }
+
+ if (op != NULL && *op != '=') {
+ if (((opt_type == gov_bool || opt_type == gov_number) && *op == '.')
+ || (opt_type == gov_string && *op != '.')) {
+ semsg(_(e_letwrong), op);
+ failed = true; // don't set the value
+ } else {
+ // number or bool
+ if (opt_type == gov_number || opt_type == gov_bool) {
+ switch (*op) {
+ case '+':
+ n = numval + n; break;
+ case '-':
+ n = numval - n; break;
+ case '*':
+ n = numval * n; break;
+ case '/':
+ n = num_divide(numval, n); break;
+ case '%':
+ n = num_modulus(numval, n); break;
+ }
+ s = NULL;
+ } else if (opt_type == gov_string && stringval != NULL && s != NULL) {
+ // string
+ char *const oldstringval = stringval;
+ stringval = concat_str(stringval, s);
+ xfree(oldstringval);
+ s = stringval;
+ }
+ }
+ }
+
+ if (!failed) {
+ if (opt_type != gov_string || s != NULL) {
+ const char *err = set_option_value(arg, (long)n, s, scope);
+ arg_end = p;
+ if (err != NULL) {
+ emsg(_(err));
+ }
+ } else {
+ emsg(_(e_stringreq));
+ }
+ }
+ *p = c1;
+ xfree(stringval);
+ xfree(tofree);
+ }
+ return arg_end;
+}
+
+/// Set a register, part of ex_let_one().
+static char *ex_let_register(char *arg, typval_T *const tv, const bool is_const,
+ const char *const endchars, const char *const op)
+ FUNC_ATTR_NONNULL_ARG(1, 2) FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ if (is_const) {
+ emsg(_("E996: Cannot lock a register"));
+ return NULL;
+ }
+
+ char *arg_end = NULL;
+ arg++;
+ if (op != NULL && vim_strchr("+-*/%", (uint8_t)(*op)) != NULL) {
+ semsg(_(e_letwrong), op);
+ } else if (endchars != NULL
+ && vim_strchr(endchars, (uint8_t)(*skipwhite(arg + 1))) == NULL) {
+ emsg(_(e_letunexp));
+ } else {
+ char *ptofree = NULL;
+ const char *p = tv_get_string_chk(tv);
+ if (p != NULL && op != NULL && *op == '.') {
+ char *s = get_reg_contents(*arg == '@' ? '"' : *arg, kGRegExprSrc);
+ if (s != NULL) {
+ ptofree = concat_str(s, p);
+ p = ptofree;
+ xfree(s);
+ }
+ }
+ if (p != NULL) {
+ write_reg_contents(*arg == '@' ? '"' : *arg, p, (ssize_t)strlen(p), false);
+ arg_end = arg + 1;
+ }
+ xfree(ptofree);
+ }
+ return arg_end;
+}
+
/// Set one item of `:let var = expr` or `:let [v1, v2] = list` to its value
///
/// @param[in] arg Start of the variable name.
@@ -718,172 +901,21 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo
{
char *arg_end = NULL;
- // ":let $VAR = expr": Set environment variable.
if (*arg == '$') {
- if (is_const) {
- emsg(_("E996: Cannot lock an environment variable"));
- return NULL;
- }
- // Find the end of the name.
- arg++;
- char *name = arg;
- int len = get_env_len((const char **)&arg);
- if (len == 0) {
- semsg(_(e_invarg2), name - 1);
- } else {
- if (op != NULL && vim_strchr("+-*/%", (uint8_t)(*op)) != NULL) {
- semsg(_(e_letwrong), op);
- } else if (endchars != NULL
- && vim_strchr(endchars, (uint8_t)(*skipwhite(arg))) == NULL) {
- emsg(_(e_letunexp));
- } else if (!check_secure()) {
- char *tofree = NULL;
- const char c1 = name[len];
- name[len] = NUL;
- const char *p = tv_get_string_chk(tv);
- if (p != NULL && op != NULL && *op == '.') {
- char *s = vim_getenv(name);
- if (s != NULL) {
- tofree = concat_str(s, p);
- p = tofree;
- xfree(s);
- }
- }
- if (p != NULL) {
- vim_setenv_ext(name, p);
- arg_end = arg;
- }
- name[len] = c1;
- xfree(tofree);
- }
- }
+ // ":let $VAR = expr": Set environment variable.
+ return ex_let_env(arg, tv, is_const, endchars, op);
+ } else if (*arg == '&') {
// ":let &option = expr": Set option value.
// ":let &l:option = expr": Set local option value.
// ":let &g:option = expr": Set global option value.
- } else if (*arg == '&') {
- if (is_const) {
- emsg(_("E996: Cannot lock an option"));
- return NULL;
- }
- // Find the end of the name.
- int scope;
- char *const p = (char *)find_option_end((const char **)&arg, &scope);
- if (p == NULL
- || (endchars != NULL
- && vim_strchr(endchars, (uint8_t)(*skipwhite(p))) == NULL)) {
- emsg(_(e_letunexp));
- } else {
- varnumber_T n = 0;
- getoption_T opt_type;
- long numval;
- char *stringval = NULL;
- const char *s = NULL;
- bool failed = false;
- uint32_t opt_p_flags;
- char *tofree = NULL;
-
- const char c1 = *p;
- *p = NUL;
-
- opt_type = get_option_value(arg, &numval, &stringval, &opt_p_flags, scope);
- if (opt_type == gov_bool
- || opt_type == gov_number
- || opt_type == gov_hidden_bool
- || opt_type == gov_hidden_number) {
- // number, possibly hidden
- n = (long)tv_get_number(tv);
- }
-
- if ((opt_p_flags & P_FUNC) && tv_is_func(*tv)) {
- // If the option can be set to a function reference or a lambda
- // and the passed value is a function reference, then convert it to
- // the name (string) of the function reference.
- s = tofree = encode_tv2string(tv, NULL);
- } else if (tv->v_type != VAR_BOOL && tv->v_type != VAR_SPECIAL) {
- // Avoid setting a string option to the text "v:false" or similar.
- s = tv_get_string_chk(tv);
- }
-
- if (op != NULL && *op != '=') {
- if (((opt_type == gov_bool || opt_type == gov_number) && *op == '.')
- || (opt_type == gov_string && *op != '.')) {
- semsg(_(e_letwrong), op);
- failed = true; // don't set the value
- } else {
- // number or bool
- if (opt_type == gov_number || opt_type == gov_bool) {
- switch (*op) {
- case '+':
- n = numval + n; break;
- case '-':
- n = numval - n; break;
- case '*':
- n = numval * n; break;
- case '/':
- n = num_divide(numval, n); break;
- case '%':
- n = num_modulus(numval, n); break;
- }
- s = NULL;
- } else if (opt_type == gov_string && stringval != NULL && s != NULL) {
- // string
- char *const oldstringval = stringval;
- stringval = concat_str(stringval, s);
- xfree(oldstringval);
- s = stringval;
- }
- }
- }
-
- if (!failed) {
- if (opt_type != gov_string || s != NULL) {
- const char *err = set_option_value(arg, (long)n, s, scope);
- arg_end = p;
- if (err != NULL) {
- emsg(_(err));
- }
- } else {
- emsg(_(e_stringreq));
- }
- }
- *p = c1;
- xfree(stringval);
- xfree(tofree);
- }
- // ":let @r = expr": Set register contents.
+ return ex_let_option(arg, tv, is_const, endchars, op);
} else if (*arg == '@') {
- if (is_const) {
- emsg(_("E996: Cannot lock a register"));
- return NULL;
- }
- arg++;
- if (op != NULL && vim_strchr("+-*/%", (uint8_t)(*op)) != NULL) {
- semsg(_(e_letwrong), op);
- } else if (endchars != NULL
- && vim_strchr(endchars, (uint8_t)(*skipwhite(arg + 1))) == NULL) {
- emsg(_(e_letunexp));
- } else {
- char *ptofree = NULL;
- const char *p = tv_get_string_chk(tv);
- if (p != NULL && op != NULL && *op == '.') {
- char *s = get_reg_contents(*arg == '@' ? '"' : *arg, kGRegExprSrc);
- if (s != NULL) {
- ptofree = concat_str(s, p);
- p = ptofree;
- xfree(s);
- }
- }
- if (p != NULL) {
- write_reg_contents(*arg == '@' ? '"' : *arg, p, (ssize_t)strlen(p), false);
- arg_end = arg + 1;
- }
- xfree(ptofree);
- }
+ // ":let @r = expr": Set register contents.
+ return ex_let_register(arg, tv, is_const, endchars, op);
+ } else if (eval_isnamec1(*arg) || *arg == '{') {
// ":let var = expr": Set internal variable.
// ":let {expr} = expr": Idem, name made with curly braces
- } else if (eval_isnamec1(*arg) || *arg == '{') {
lval_T lv;
-
char *const p = get_lval(arg, tv, &lv, false, false, 0, FNE_CHECK_START);
if (p != NULL && lv.ll_name != NULL) {
if (endchars != NULL && vim_strchr(endchars, (uint8_t)(*skipwhite(p))) == NULL) {
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 1d8c3c0cf4..40afb3250c 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -3280,9 +3280,11 @@ static int check_regexp_delim(int c)
///
/// The usual escapes are supported as described in the regexp docs.
///
-/// @param do_buf_event If `true`, send buffer updates.
+/// @param cmdpreview_ns The namespace to show 'inccommand' preview highlights.
+/// If <= 0, preview shouldn't be shown.
/// @return 0, 1 or 2. See show_cmdpreview() for more information on what the return value means.
-static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T cmdpreview_bufnr)
+static int do_sub(exarg_T *eap, const proftime_T timeout, const long cmdpreview_ns,
+ const handle_T cmdpreview_bufnr)
{
#define ADJUST_SUB_FIRSTLNUM() \
do { \
@@ -3400,7 +3402,7 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
MB_PTR_ADV(cmd);
}
- if (!eap->skip && !cmdpreview) {
+ if (!eap->skip && cmdpreview_ns <= 0) {
sub_set_replacement((SubReplacementString) {
.sub = xstrdup(sub),
.timestamp = os_time(),
@@ -3420,7 +3422,7 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
endcolumn = (curwin->w_curswant == MAXCOL);
}
- if (sub != NULL && sub_joining_lines(eap, pat, sub, cmd, !cmdpreview)) {
+ if (sub != NULL && sub_joining_lines(eap, pat, sub, cmd, cmdpreview_ns <= 0)) {
return 0;
}
@@ -3465,7 +3467,7 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
}
if (search_regcomp(pat, NULL, RE_SUBST, which_pat,
- (cmdpreview ? 0 : SEARCH_HIS), &regmatch) == FAIL) {
+ (cmdpreview_ns > 0 ? 0 : SEARCH_HIS), &regmatch) == FAIL) {
if (subflags.do_error) {
emsg(_(e_invcmd));
}
@@ -3494,7 +3496,7 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
sub = xstrdup(sub);
sub_copy = sub;
} else {
- char *newsub = regtilde(sub, magic_isset(), cmdpreview);
+ char *newsub = regtilde(sub, magic_isset(), cmdpreview_ns > 0);
if (newsub != sub) {
// newsub was allocated, free it later.
sub_copy = newsub;
@@ -3508,7 +3510,7 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
for (linenr_T lnum = eap->line1;
lnum <= line2 && !got_quit && !aborting()
- && (!cmdpreview || preview_lines.lines_needed <= (linenr_T)p_cwh
+ && (cmdpreview_ns <= 0 || preview_lines.lines_needed <= (linenr_T)p_cwh
|| lnum <= curwin->w_botline);
lnum++) {
long nmatch = vim_regexec_multi(&regmatch, curwin, curbuf, lnum,
@@ -3669,7 +3671,7 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
}
}
- if (subflags.do_ask && !cmdpreview) {
+ if (subflags.do_ask && cmdpreview_ns <= 0) {
int typed = 0;
// change State to MODE_CONFIRM, so that the mouse works
@@ -3882,7 +3884,7 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
// Save the line numbers for the preview buffer
// NOTE: If the pattern matches a final newline, the next line will
// be shown also, but should not be highlighted. Intentional for now.
- if (cmdpreview && !has_second_delim) {
+ if (cmdpreview_ns > 0 && !has_second_delim) {
current_match.start.col = regmatch.startpos[0].col;
if (current_match.end.lnum == 0) {
current_match.end.lnum = sub_firstlnum + (linenr_T)nmatch - 1;
@@ -3897,7 +3899,7 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
// 3. Substitute the string. During 'inccommand' preview only do this if
// there is a replace pattern.
- if (!cmdpreview || has_second_delim) {
+ if (cmdpreview_ns <= 0 || has_second_delim) {
long lnum_start = lnum; // save the start lnum
int save_ma = curbuf->b_p_ma;
int save_sandbox = sandbox;
@@ -4147,7 +4149,7 @@ skip:
#define PUSH_PREVIEW_LINES() \
do { \
- if (cmdpreview) { \
+ if (cmdpreview_ns > 0) { \
linenr_T match_lines = current_match.end.lnum \
- current_match.start.lnum +1; \
if (preview_lines.subresults.size > 0) { \
@@ -4230,7 +4232,7 @@ skip:
beginline(BL_WHITE | BL_FIX);
}
}
- if (!cmdpreview && !do_sub_msg(subflags.do_count) && subflags.do_ask && p_ch > 0) {
+ if (cmdpreview_ns <= 0 && !do_sub_msg(subflags.do_count) && subflags.do_ask && p_ch > 0) {
msg("");
}
} else {
@@ -4269,7 +4271,7 @@ skip:
int retv = 0;
// Show 'inccommand' preview if there are matched lines.
- if (cmdpreview && !aborting()) {
+ if (cmdpreview_ns > 0 && !aborting()) {
if (got_quit || profile_passed_limit(timeout)) { // Too slow, disable.
set_string_option_direct("icm", -1, "", OPT_FREE, SID_NONE);
} else if (*p_icm != NUL && pat != NULL) {
diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua
index b0938fa711..88f3bc0b43 100644
--- a/src/nvim/ex_cmds.lua
+++ b/src/nvim/ex_cmds.lua
@@ -2212,7 +2212,7 @@ module.cmds = {
},
{
command='registers',
- flags=bit.bor(EXTRA, NOTRLCOM, TRLBAR, CMDWIN, LOCK_OK),
+ flags=bit.bor(EXTRA, NOTRLCOM, TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_display',
},
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index dd23f6ece9..b2acc561be 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -1597,7 +1597,7 @@ static int command_line_insert_reg(CommandLineState *s)
bool literally = false;
if (s->c != ESC) { // use ESC to cancel inserting register
- literally = i == Ctrl_R;
+ literally = i == Ctrl_R || is_literal_register(s->c);
cmdline_paste(s->c, literally, false);
// When there was a serious error abort getting the
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index e60007bf88..d24ac1c233 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -153,6 +153,7 @@ void filemess(buf_T *buf, char *name, char *s, int attr)
int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
linenr_T lines_to_read, exarg_T *eap, int flags, bool silent)
{
+ int retval = FAIL; // jump to "theend" instead of returning
int fd = stdin_fd >= 0 ? stdin_fd : 0;
int newfile = (flags & READ_NEW);
int check_readonly;
@@ -240,7 +241,7 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
&& vim_strchr(p_cpo, CPO_FNAMER) != NULL
&& !(flags & READ_DUMMY)) {
if (set_rw_fname(fname, sfname) == FAIL) {
- return FAIL;
+ goto theend;
}
}
@@ -284,10 +285,9 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
if (newfile) {
if (apply_autocmds_exarg(EVENT_BUFREADCMD, NULL, sfname,
false, curbuf, eap)) {
- int status = OK;
-
+ retval = OK;
if (aborting()) {
- status = FAIL;
+ retval = FAIL;
}
// The BufReadCmd code usually uses ":read" to get the text and
@@ -295,14 +295,15 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
// consider this to work like ":edit", thus reset the
// BF_NOTEDITED flag. Then ":write" will work to overwrite the
// same file.
- if (status == OK) {
+ if (retval == OK) {
curbuf->b_flags &= ~BF_NOTEDITED;
}
- return status;
+ goto theend;
}
} else if (apply_autocmds_exarg(EVENT_FILEREADCMD, sfname, sfname,
false, NULL, eap)) {
- return aborting() ? FAIL : OK;
+ retval = aborting() ? FAIL : OK;
+ goto theend;
}
curbuf->b_op_start = orig_start;
@@ -310,7 +311,8 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
if (flags & READ_NOFILE) {
// Return NOTDONE instead of FAIL so that BufEnter can be triggered
// and other operations don't fail.
- return NOTDONE;
+ retval = NOTDONE;
+ goto theend;
}
}
@@ -328,7 +330,7 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
filemess(curbuf, fname, _("Illegal file name"), 0);
msg_end();
msg_scroll = msg_save;
- return FAIL;
+ goto theend;
}
// If the name ends in a path separator, we can't open it. Check here,
@@ -340,7 +342,8 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
}
msg_end();
msg_scroll = msg_save;
- return NOTDONE;
+ retval = NOTDONE;
+ goto theend;
}
}
@@ -365,12 +368,13 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
if (!silent) {
filemess(curbuf, fname, _(msg_is_a_directory), 0);
}
+ retval = NOTDONE;
} else {
filemess(curbuf, fname, _("is not a file"), 0);
}
msg_end();
msg_scroll = msg_save;
- return S_ISDIR(perm) ? NOTDONE : FAIL;
+ goto theend;
}
}
@@ -431,7 +435,7 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
if (fd < 0) { // cannot open at all
msg_scroll = msg_save;
if (!newfile) {
- return FAIL;
+ goto theend;
}
if (perm == UV_ENOENT) { // check if the file exists
// Set the 'new-file' flag, so that when the file has
@@ -450,7 +454,7 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
|| (using_b_fname
&& (old_b_fname != curbuf->b_fname))) {
emsg(_(e_auchangedbuf));
- return FAIL;
+ goto theend;
}
}
if (!silent) {
@@ -472,10 +476,10 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
// remember the current fileformat
save_file_ff(curbuf);
- if (aborting()) { // autocmds may abort script processing
- return FAIL;
+ if (!aborting()) { // autocmds may abort script processing
+ retval = OK; // a new file is not an error
}
- return OK; // a new file is not an error
+ goto theend;
}
#if defined(UNIX) && defined(EOVERFLOW)
filemess(curbuf, sfname, ((fd == UV_EFBIG) ? _("[File too big]") :
@@ -491,7 +495,7 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
#endif
curbuf->b_p_ro = true; // must use "w!" now
- return FAIL;
+ goto theend;
}
// Only set the 'ro' flag for readonly files the first time they are
@@ -526,7 +530,7 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
if (!read_buffer) {
close(fd);
}
- return FAIL;
+ goto theend;
}
#ifdef UNIX
// Set swap file protection bits after creating it.
@@ -561,7 +565,7 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
if (!read_buffer && !read_stdin) {
close(fd);
}
- return FAIL;
+ goto theend;
}
no_wait_return++; // don't wait for return yet
@@ -617,7 +621,7 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
no_wait_return--;
msg_scroll = msg_save;
curbuf->b_p_ro = true; // must use "w!" now
- return FAIL;
+ goto theend;
}
// Don't allow the autocommands to change the current buffer.
// Try to re-open the file.
@@ -636,7 +640,7 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
emsg(_("E201: *ReadPre autocommands must not change current buffer"));
}
curbuf->b_p_ro = true; // must use "w!" now
- return FAIL;
+ goto theend;
}
}
@@ -1684,7 +1688,8 @@ failed:
}
msg_scroll = msg_save;
check_marks_read();
- return OK; // an interrupt isn't really an error
+ retval = OK; // an interrupt isn't really an error
+ goto theend;
}
if (!filtering && !(flags & READ_DUMMY) && !silent) {
@@ -1860,10 +1865,18 @@ failed:
}
}
- if (recoverymode && error) {
- return FAIL;
+ if (!(recoverymode && error)) {
+ retval = OK;
}
- return OK;
+
+theend:
+ if (curbuf->b_ml.ml_mfp != NULL
+ && curbuf->b_ml.ml_mfp->mf_dirty == MF_DIRTY_YES_NOSYNC) {
+ // OK to sync the swap file now
+ curbuf->b_ml.ml_mfp->mf_dirty = MF_DIRTY_YES;
+ }
+
+ return retval;
}
#ifdef OPEN_CHR_FILES
diff --git a/src/nvim/main.c b/src/nvim/main.c
index d4fbf8ce93..f27ebb2f67 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -250,6 +250,11 @@ int main(int argc, char **argv)
argv0 = argv[0];
+ if (!appname_is_valid()) {
+ os_errmsg("$NVIM_APPNAME is not a valid file name.\n");
+ exit(1);
+ }
+
if (argc > 1 && STRICMP(argv[1], "-ll") == 0) {
if (argc == 2) {
print_mainerr(err_arg_missing, argv[1]);
diff --git a/src/nvim/memfile.c b/src/nvim/memfile.c
index bd8314c679..5d6c58c387 100644
--- a/src/nvim/memfile.c
+++ b/src/nvim/memfile.c
@@ -103,7 +103,7 @@ memfile_T *mf_open(char *fname, int flags)
mfp->mf_free_first = NULL; // free list is empty
mfp->mf_used_first = NULL; // used list is empty
mfp->mf_used_last = NULL;
- mfp->mf_dirty = false;
+ mfp->mf_dirty = MF_DIRTY_NO;
mf_hash_init(&mfp->mf_hash);
mf_hash_init(&mfp->mf_trans);
mfp->mf_page_size = MEMFILE_PAGE_SIZE;
@@ -159,7 +159,7 @@ memfile_T *mf_open(char *fname, int flags)
int mf_open_file(memfile_T *mfp, char *fname)
{
if (mf_do_open(mfp, fname, O_RDWR | O_CREAT | O_EXCL)) {
- mfp->mf_dirty = true;
+ mfp->mf_dirty = MF_DIRTY_YES;
return OK;
}
@@ -269,7 +269,7 @@ bhdr_T *mf_new(memfile_T *mfp, bool negative, unsigned page_count)
}
}
hp->bh_flags = BH_LOCKED | BH_DIRTY; // new block is always dirty
- mfp->mf_dirty = true;
+ mfp->mf_dirty = MF_DIRTY_YES;
hp->bh_page_count = page_count;
mf_ins_used(mfp, hp);
mf_ins_hash(mfp, hp);
@@ -342,7 +342,9 @@ void mf_put(memfile_T *mfp, bhdr_T *hp, bool dirty, bool infile)
flags &= ~BH_LOCKED;
if (dirty) {
flags |= BH_DIRTY;
- mfp->mf_dirty = true;
+ if (mfp->mf_dirty != MF_DIRTY_YES_NOSYNC) {
+ mfp->mf_dirty = MF_DIRTY_YES;
+ }
}
hp->bh_flags = flags;
if (infile) {
@@ -382,8 +384,9 @@ int mf_sync(memfile_T *mfp, int flags)
{
int got_int_save = got_int;
- if (mfp->mf_fd < 0) { // there is no file, nothing to do
- mfp->mf_dirty = false;
+ if (mfp->mf_fd < 0) {
+ // there is no file, nothing to do
+ mfp->mf_dirty = MF_DIRTY_NO;
return FAIL;
}
@@ -426,7 +429,7 @@ int mf_sync(memfile_T *mfp, int flags)
// If the whole list is flushed, the memfile is not dirty anymore.
// In case of an error, dirty flag is also set, to avoid trying all the time.
if (hp == NULL || status == FAIL) {
- mfp->mf_dirty = false;
+ mfp->mf_dirty = MF_DIRTY_NO;
}
if (flags & MFS_FLUSH) {
@@ -449,7 +452,7 @@ void mf_set_dirty(memfile_T *mfp)
hp->bh_flags |= BH_DIRTY;
}
}
- mfp->mf_dirty = true;
+ mfp->mf_dirty = MF_DIRTY_YES;
}
/// Insert block in front of memfile's hash list.
diff --git a/src/nvim/memfile_defs.h b/src/nvim/memfile_defs.h
index 53152c28f8..917dd6a905 100644
--- a/src/nvim/memfile_defs.h
+++ b/src/nvim/memfile_defs.h
@@ -38,13 +38,13 @@ typedef struct mf_hashitem {
/// mf_hashitem_T which contains the key and linked list pointers. List of items
/// in each bucket is doubly-linked.
typedef struct mf_hashtab {
- size_t mht_mask; /// mask used to mod hash value to array index
- /// (nr of items in array is 'mht_mask + 1')
- size_t mht_count; /// number of items inserted
- mf_hashitem_T **mht_buckets; /// points to the array of buckets (can be
- /// mht_small_buckets or a newly allocated array
- /// when mht_small_buckets becomes too small)
- mf_hashitem_T *mht_small_buckets[MHT_INIT_SIZE]; /// initial buckets
+ size_t mht_mask; ///< mask used to mod hash value to array index
+ ///< (nr of items in array is 'mht_mask + 1')
+ size_t mht_count; ///< number of items inserted
+ mf_hashitem_T **mht_buckets; ///< points to the array of buckets (can be
+ ///< mht_small_buckets or a newly allocated array
+ ///< when mht_small_buckets becomes too small)
+ mf_hashitem_T *mht_small_buckets[MHT_INIT_SIZE]; ///< initial buckets
} mf_hashtab_T;
/// A block header.
@@ -61,17 +61,17 @@ typedef struct mf_hashtab {
/// The blocks in the free list have no block of memory allocated and
/// the contents of the block in the file (if any) is irrelevant.
typedef struct bhdr {
- mf_hashitem_T bh_hashitem; /// header for hash table and key
-#define bh_bnum bh_hashitem.mhi_key /// block number, part of bh_hashitem
+ mf_hashitem_T bh_hashitem; ///< header for hash table and key
+#define bh_bnum bh_hashitem.mhi_key ///< block number, part of bh_hashitem
- struct bhdr *bh_next; /// next block header in free or used list
- struct bhdr *bh_prev; /// previous block header in used list
- void *bh_data; /// pointer to memory (for used block)
- unsigned bh_page_count; /// number of pages in this block
+ struct bhdr *bh_next; ///< next block header in free or used list
+ struct bhdr *bh_prev; ///< previous block header in used list
+ void *bh_data; ///< pointer to memory (for used block)
+ unsigned bh_page_count; ///< number of pages in this block
#define BH_DIRTY 1U
#define BH_LOCKED 2U
- unsigned bh_flags; // BH_DIRTY or BH_LOCKED
+ unsigned bh_flags; ///< BH_DIRTY or BH_LOCKED
} bhdr_T;
/// A block number translation list item.
@@ -81,27 +81,33 @@ typedef struct bhdr {
/// number, we remember the translation to the new positive number in the
/// double linked trans lists. The structure is the same as the hash lists.
typedef struct mf_blocknr_trans_item {
- mf_hashitem_T nt_hashitem; /// header for hash table and key
-#define nt_old_bnum nt_hashitem.mhi_key /// old, negative, number
- blocknr_T nt_new_bnum; /// new, positive, number
+ mf_hashitem_T nt_hashitem; ///< header for hash table and key
+#define nt_old_bnum nt_hashitem.mhi_key ///< old, negative, number
+ blocknr_T nt_new_bnum; ///< new, positive, number
} mf_blocknr_trans_item_T;
+typedef enum {
+ MF_DIRTY_NO = 0, ///< no dirty blocks
+ MF_DIRTY_YES, ///< there are dirty blocks
+ MF_DIRTY_YES_NOSYNC, ///< there are dirty blocks, do not sync yet
+} mfdirty_T;
+
/// A memory file.
typedef struct memfile {
- char *mf_fname; /// name of the file
- char *mf_ffname; /// idem, full path
- int mf_fd; /// file descriptor
- bhdr_T *mf_free_first; /// first block header in free list
- bhdr_T *mf_used_first; /// mru block header in used list
- bhdr_T *mf_used_last; /// lru block header in used list
- mf_hashtab_T mf_hash; /// hash lists
- mf_hashtab_T mf_trans; /// trans lists
- blocknr_T mf_blocknr_max; /// highest positive block number + 1
- blocknr_T mf_blocknr_min; /// lowest negative block number - 1
- blocknr_T mf_neg_count; /// number of negative blocks numbers
- blocknr_T mf_infile_count; /// number of pages in the file
- unsigned mf_page_size; /// number of bytes in a page
- bool mf_dirty; /// true if there are dirty blocks
+ char *mf_fname; ///< name of the file
+ char *mf_ffname; ///< idem, full path
+ int mf_fd; ///< file descriptor
+ bhdr_T *mf_free_first; ///< first block header in free list
+ bhdr_T *mf_used_first; ///< mru block header in used list
+ bhdr_T *mf_used_last; ///< lru block header in used list
+ mf_hashtab_T mf_hash; ///< hash lists
+ mf_hashtab_T mf_trans; ///< trans lists
+ blocknr_T mf_blocknr_max; ///< highest positive block number + 1
+ blocknr_T mf_blocknr_min; ///< lowest negative block number - 1
+ blocknr_T mf_neg_count; ///< number of negative blocks numbers
+ blocknr_T mf_infile_count; ///< number of pages in the file
+ unsigned mf_page_size; ///< number of bytes in a page
+ mfdirty_T mf_dirty;
} memfile_T;
#endif // NVIM_MEMFILE_DEFS_H
diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index 0815ba411c..eb2afc60b2 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -510,6 +510,8 @@ void ml_open_file(buf_T *buf)
continue;
}
if (mf_open_file(mfp, fname) == OK) { // consumes fname!
+ // don't sync yet in ml_sync_all()
+ mfp->mf_dirty = MF_DIRTY_YES_NOSYNC;
ml_upd_block0(buf, UB_SAME_DIR);
// Flush block zero, so others can read it
@@ -1714,7 +1716,7 @@ void ml_sync_all(int check_file, int check_char, bool do_fsync)
need_check_timestamps = true; // give message later
}
}
- if (buf->b_ml.ml_mfp->mf_dirty) {
+ if (buf->b_ml.ml_mfp->mf_dirty == MF_DIRTY_YES) {
(void)mf_sync(buf->b_ml.ml_mfp, (check_char ? MFS_STOP : 0)
| (do_fsync && bufIsChanged(buf) ? MFS_FLUSH : 0));
if (check_char && os_char_avail()) { // character available now
diff --git a/src/nvim/memory.c b/src/nvim/memory.c
index 1f550ffb01..6b4d290863 100644
--- a/src/nvim/memory.c
+++ b/src/nvim/memory.c
@@ -816,7 +816,6 @@ void free_all_mem(void)
grid_free_all_mem();
clear_hl_tables(false);
- list_free_log();
check_quickfix_busy();
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index de77cdd238..c39a3273da 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -854,15 +854,6 @@ static bool is_append_register(int regname)
return ASCII_ISUPPER(regname);
}
-/// @see get_yank_register
-/// @returns true when register should be inserted literally
-/// (selection or clipboard)
-static inline bool is_literal_register(int regname)
- FUNC_ATTR_CONST
-{
- return regname == '*' || regname == '+';
-}
-
/// @return a copy of contents in register `name` for use in do_put. Should be
/// freed by caller.
yankreg_T *copy_register(int name)
diff --git a/src/nvim/ops.h b/src/nvim/ops.h
index 81e006be27..e378d2f7b5 100644
--- a/src/nvim/ops.h
+++ b/src/nvim/ops.h
@@ -123,6 +123,15 @@ static inline int op_reg_index(const int regname)
}
}
+/// @see get_yank_register
+/// @return true when register should be inserted literally
+/// (selection or clipboard)
+static inline bool is_literal_register(const int regname)
+ FUNC_ATTR_CONST
+{
+ return regname == '*' || regname == '+';
+}
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ops.h.generated.h"
#endif
diff --git a/src/nvim/os/stdpaths.c b/src/nvim/os/stdpaths.c
index 8b62b9e895..53ddda22fa 100644
--- a/src/nvim/os/stdpaths.c
+++ b/src/nvim/os/stdpaths.c
@@ -69,6 +69,19 @@ const char *get_appname(void)
return env_val;
}
+/// Ensure that APPNAME is valid. In particular, it cannot contain directory separators.
+bool appname_is_valid(void)
+{
+ const char *appname = get_appname();
+ const size_t appname_len = strlen(appname);
+ for (size_t i = 0; i < appname_len; i++) {
+ if (appname[i] == PATHSEP) {
+ return false;
+ }
+ }
+ return true;
+}
+
/// Return XDG variable value
///
/// @param[in] idx XDG variable to use.
diff --git a/src/nvim/po/check.vim b/src/nvim/po/check.vim
index 7705ba8577..8752af663b 100644
--- a/src/nvim/po/check.vim
+++ b/src/nvim/po/check.vim
@@ -6,6 +6,9 @@
if 1 " Only execute this if the eval feature is available.
+" using line continuation
+set cpo&vim
+
let filename = "check-" . expand("%:t:r") . ".log"
exe 'redir! > ' . filename
@@ -62,12 +65,18 @@ while 1
if getline(line('.') - 1) !~ "no-c-format"
" go over the "msgid" and "msgid_plural" lines
let prevfromline = 'foobar'
+ let plural = 0
while 1
+ if getline('.') =~ 'msgid_plural'
+ let plural += 1
+ endif
let fromline = GetMline()
if prevfromline != 'foobar' && prevfromline != fromline
+ \ && (plural != 1
+ \ || count(prevfromline, '%') + 1 != count(fromline, '%'))
echomsg 'Mismatching % in line ' . (line('.') - 1)
echomsg 'msgid: ' . prevfromline
- echomsg 'msgid ' . fromline
+ echomsg 'msgid: ' . fromline
if error == 0
let error = line('.')
endif
@@ -89,6 +98,7 @@ while 1
while getline('.') =~ '^msgstr'
let toline = GetMline()
if fromline != toline
+ \ && (plural == 0 || count(fromline, '%') != count(toline, '%') + 1)
echomsg 'Mismatching % in line ' . (line('.') - 1)
echomsg 'msgid: ' . fromline
echomsg 'msgstr: ' . toline
diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c
index 015c578396..b89d346fbf 100644
--- a/src/nvim/statusline.c
+++ b/src/nvim/statusline.c
@@ -1031,6 +1031,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n
int evaldepth = 0;
int curitem = 0;
+ int foldsignitem = -1;
bool prevchar_isflag = true;
bool prevchar_isitem = false;
@@ -1655,6 +1656,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n
if (width == 0) {
break;
}
+ foldsignitem = curitem;
char *p = NULL;
if (fold) {
@@ -1664,32 +1666,22 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n
p[n] = NUL;
}
- *buf_tmp = NUL;
+ size_t buflen = 0;
varnumber_T virtnum = get_vim_var_nr(VV_VIRTNUM);
- for (int i = 0; i <= width; i++) {
- if (i == width) {
- if (*buf_tmp == NUL) {
- break;
- }
- stl_items[curitem].minwid = 0;
- } else if (!fold) {
+ for (int i = 0; i < width; i++) {
+ if (!fold) {
SignTextAttrs *sattr = virtnum ? NULL : sign_get_attr(i, stcp->sattrs, wp->w_scwidth);
p = sattr && sattr->text ? sattr->text : " ";
stl_items[curitem].minwid = -(sattr ? stcp->sign_cul_id ? stcp->sign_cul_id
: sattr->hl_id : (stcp->use_cul ? HLF_CLS : HLF_SC) + 1);
}
- size_t buflen = strlen(buf_tmp);
stl_items[curitem].type = Highlight;
stl_items[curitem].start = out_p + buflen;
+ xstrlcpy(buf_tmp + buflen, p, TMPLEN - buflen);
+ buflen += strlen(p);
curitem++;
- if (i == width) {
- str = buf_tmp;
- break;
- }
- int rc = snprintf(buf_tmp + buflen, sizeof(buf_tmp) - buflen, "%s", p);
- (void)rc; // Avoid unused warning on release build
- assert(rc > 0);
}
+ str = buf_tmp;
break;
}
@@ -1832,6 +1824,13 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n
}
}
minwid = 0;
+ // For a 'statuscolumn' sign or fold item, shift the added items
+ if (foldsignitem >= 0) {
+ ptrdiff_t offset = out_p - stl_items[foldsignitem].start;
+ for (int i = foldsignitem; i < curitem; i++) {
+ stl_items[i].start += offset;
+ }
+ }
} else {
// Note: The negative value denotes a left aligned item.
// Here we switch the minimum width back to a positive value.
@@ -1851,6 +1850,14 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n
}
// }
+ // For a 'statuscolumn' sign or fold item, add an item to reset the highlight group
+ if (foldsignitem >= 0) {
+ foldsignitem = -1;
+ stl_items[curitem].type = Highlight;
+ stl_items[curitem].start = out_p;
+ stl_items[curitem].minwid = 0;
+ }
+
// For left-aligned items, fill any remaining space with the fillchar
for (; l < minwid && out_p < out_end_p; l++) {
MB_CHAR2BYTES(fillchar, out_p);
diff --git a/src/nvim/testing.c b/src/nvim/testing.c
index 25ec8e898a..eea75f3021 100644
--- a/src/nvim/testing.c
+++ b/src/nvim/testing.c
@@ -775,5 +775,4 @@ void f_test_write_list_log(typval_T *const argvars, typval_T *const rettv, EvalF
if (fname == NULL) {
return;
}
- list_write_log(fname);
}
diff --git a/test/functional/autocmd/textyankpost_spec.lua b/test/functional/autocmd/textyankpost_spec.lua
index 3898d59e58..1640916ad8 100644
--- a/test/functional/autocmd/textyankpost_spec.lua
+++ b/test/functional/autocmd/textyankpost_spec.lua
@@ -8,7 +8,7 @@ describe('TextYankPost', function()
clear()
-- emulate the clipboard so system clipboard isn't affected
- command('let &rtp = "test/functional/fixtures,".&rtp')
+ command('set rtp^=test/functional/fixtures')
command('let g:count = 0')
command('autocmd TextYankPost * let g:event = copy(v:event)')
diff --git a/test/functional/options/defaults_spec.lua b/test/functional/options/defaults_spec.lua
index 3690b7e97c..60edf219d9 100644
--- a/test/functional/options/defaults_spec.lua
+++ b/test/functional/options/defaults_spec.lua
@@ -14,6 +14,7 @@ local ok = helpers.ok
local funcs = helpers.funcs
local insert = helpers.insert
local neq = helpers.neq
+local nvim_prog = helpers.nvim_prog
local mkdir = helpers.mkdir
local rmdir = helpers.rmdir
local alter_slashes = helpers.alter_slashes
@@ -603,6 +604,10 @@ describe('stdpath()', function()
eq(appname, funcs.fnamemodify(funcs.stdpath('data_dirs')[1], ':t'))
end
assert_alive() -- Check for crash. #8393
+
+ -- Check that nvim rejects invalid APPNAMEs
+ local child = funcs.jobstart({ nvim_prog }, {env={NVIM_APPNAME='a/b\\c'}})
+ eq(1, funcs.jobwait({child}, 3000)[1])
end)
context('returns a String', function()
diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua
index b906ae265f..e0ce62c0db 100644
--- a/test/functional/plugin/lsp_spec.lua
+++ b/test/functional/plugin/lsp_spec.lua
@@ -948,7 +948,7 @@ describe('LSP', function()
test_name = "check_tracked_requests_cleared";
on_init = function(_client)
command('let g:requests = 0')
- command('autocmd User LspRequest let g:requests+=1')
+ command('autocmd LspRequest * let g:requests+=1')
client = _client
client.request("slow_request")
eq(1, eval('g:requests'))
@@ -3765,6 +3765,96 @@ describe('LSP', function()
end)
end)
+ describe('#dynamic vim.lsp._dynamic', function()
+ it('supports dynamic registration', function()
+ local root_dir = helpers.tmpname()
+ os.remove(root_dir)
+ mkdir(root_dir)
+ local tmpfile = root_dir .. '/dynamic.foo'
+ local file = io.open(tmpfile, 'w')
+ file:close()
+
+ exec_lua(create_server_definition)
+ local result = exec_lua([[
+ local root_dir, tmpfile = ...
+
+ local server = _create_server()
+ local client_id = vim.lsp.start({
+ name = 'dynamic-test',
+ cmd = server.cmd,
+ root_dir = root_dir,
+ capabilities = {
+ textDocument = {
+ formatting = {
+ dynamicRegistration = true,
+ },
+ rangeFormatting = {
+ dynamicRegistration = true,
+ },
+ },
+ },
+ })
+
+ local expected_messages = 2 -- initialize, initialized
+
+ vim.lsp.handlers['client/registerCapability'](nil, {
+ registrations = {
+ {
+ id = 'formatting',
+ method = 'textDocument/formatting',
+ registerOptions = {
+ documentSelector = {{
+ pattern = root_dir .. '/*.foo',
+ }},
+ },
+ },
+ },
+ }, { client_id = client_id })
+
+ vim.lsp.handlers['client/registerCapability'](nil, {
+ registrations = {
+ {
+ id = 'range-formatting',
+ method = 'textDocument/rangeFormatting',
+ },
+ },
+ }, { client_id = client_id })
+
+ vim.lsp.handlers['client/registerCapability'](nil, {
+ registrations = {
+ {
+ id = 'completion',
+ method = 'textDocument/completion',
+ },
+ },
+ }, { client_id = client_id })
+
+ local result = {}
+ local function check(method, fname)
+ local bufnr = fname and vim.fn.bufadd(fname) or nil
+ local client = vim.lsp.get_client_by_id(client_id)
+ result[#result + 1] = {method = method, fname = fname, supported = client.supports_method(method, {bufnr = bufnr})}
+ end
+
+
+ check("textDocument/formatting")
+ check("textDocument/formatting", tmpfile)
+ check("textDocument/rangeFormatting")
+ check("textDocument/rangeFormatting", tmpfile)
+ check("textDocument/completion")
+
+ return result
+ ]], root_dir, tmpfile)
+
+ eq(5, #result)
+ eq({method = 'textDocument/formatting', supported = false}, result[1])
+ eq({method = 'textDocument/formatting', supported = true, fname = tmpfile}, result[2])
+ eq({method = 'textDocument/rangeFormatting', supported = true}, result[3])
+ eq({method = 'textDocument/rangeFormatting', supported = true, fname = tmpfile}, result[4])
+ eq({method = 'textDocument/completion', supported = false}, result[5])
+ end)
+ end)
+
describe('vim.lsp._watchfiles', function()
it('sends notifications when files change', function()
local root_dir = helpers.tmpname()
diff --git a/test/functional/provider/clipboard_spec.lua b/test/functional/provider/clipboard_spec.lua
index 2c5185a974..c8f1518283 100644
--- a/test/functional/provider/clipboard_spec.lua
+++ b/test/functional/provider/clipboard_spec.lua
@@ -301,7 +301,7 @@ end)
describe('clipboard (with fake clipboard.vim)', function()
local function reset(...)
- clear('--cmd', 'let &rtp = "test/functional/fixtures,".&rtp', ...)
+ clear('--cmd', 'set rtp^=test/functional/fixtures', ...)
end
before_each(function()
diff --git a/test/functional/provider/provider_spec.lua b/test/functional/provider/provider_spec.lua
index 3895b8613f..b1c326d04c 100644
--- a/test/functional/provider/provider_spec.lua
+++ b/test/functional/provider/provider_spec.lua
@@ -7,7 +7,7 @@ local pcall_err = helpers.pcall_err
describe('providers', function()
before_each(function()
- clear('--cmd', 'let &rtp = "test/functional/fixtures,".&rtp')
+ clear('--cmd', 'set rtp^=test/functional/fixtures')
end)
it('with #Call(), missing g:loaded_xx_provider', function()
diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua
index b9080cbcf5..54441984a3 100644
--- a/test/functional/ui/decorations_spec.lua
+++ b/test/functional/ui/decorations_spec.lua
@@ -8,6 +8,7 @@ local exec_lua = helpers.exec_lua
local exec = helpers.exec
local expect_events = helpers.expect_events
local meths = helpers.meths
+local funcs = helpers.funcs
local curbufmeths = helpers.curbufmeths
local command = helpers.command
local assert_alive = helpers.assert_alive
@@ -651,7 +652,7 @@ describe('extmark decorations', function()
[16] = {blend = 30, background = Screen.colors.Red1, foreground = Screen.colors.Magenta1};
[17] = {bold = true, foreground = Screen.colors.Brown, background = Screen.colors.LightGrey};
[18] = {background = Screen.colors.LightGrey};
- [19] = {foreground = Screen.colors.Cyan4, background = Screen.colors.LightGrey};
+ [19] = {foreground = Screen.colors.DarkCyan, background = Screen.colors.LightGrey};
[20] = {foreground = tonumber('0x180606'), background = tonumber('0xf13f3f')};
[21] = {foreground = Screen.colors.Gray0, background = tonumber('0xf13f3f')};
[22] = {foreground = tonumber('0xb20000'), background = tonumber('0xf13f3f')};
@@ -662,6 +663,9 @@ describe('extmark decorations', function()
[27] = {background = Screen.colors.Plum1};
[28] = {underline = true, foreground = Screen.colors.SlateBlue};
[29] = {foreground = Screen.colors.SlateBlue, background = Screen.colors.LightGray, underline = true};
+ [30] = {foreground = Screen.colors.DarkCyan, background = Screen.colors.LightGray, underline = true};
+ [31] = {underline = true, foreground = Screen.colors.DarkCyan};
+ [32] = {underline = true};
}
ns = meths.create_namespace 'test'
@@ -827,26 +831,62 @@ describe('extmark decorations', function()
end -- ?古古古古?古古 |
|
]]}
+ end)
- screen:try_resize(50, 2)
+ it('virt_text_hide hides overlay virtual text when extmark is off-screen', function()
+ screen:try_resize(50, 3)
command('set nowrap')
- meths.buf_set_lines(0, 12, 12, true, {'-- ' .. ('…'):rep(57)})
- feed('G')
- meths.buf_set_extmark(0, ns, 12, 123, { virt_text={{'!!!!!', 'ErrorMsg'}}, virt_text_pos='overlay', virt_text_hide=true})
+ meths.buf_set_lines(0, 0, -1, true, {'-- ' .. ('…'):rep(57)})
+ meths.buf_set_extmark(0, ns, 0, 0, { virt_text={{'?????', 'ErrorMsg'}}, virt_text_pos='overlay', virt_text_hide=true})
+ meths.buf_set_extmark(0, ns, 0, 123, { virt_text={{'!!!!!', 'ErrorMsg'}}, virt_text_pos='overlay', virt_text_hide=true})
screen:expect{grid=[[
- ^-- …………………………………………………………………………………………………………{4:!!!!!}……|
+ {4:^?????}……………………………………………………………………………………………………{4:!!!!!}……|
+ {1:~ }|
|
]]}
feed('40zl')
screen:expect{grid=[[
^………{4:!!!!!}……………………………… |
+ {1:~ }|
+ |
+ ]]}
+ feed('3zl')
+ screen:expect{grid=[[
+ {4:^!!!!!}……………………………… |
+ {1:~ }|
+ |
+ ]]}
+ feed('7zl')
+ screen:expect{grid=[[
+ ^………………………… |
+ {1:~ }|
|
]]}
- feed('10zl')
+
+ command('set wrap smoothscroll')
screen:expect{grid=[[
+ {4:?????}……………………………………………………………………………………………………{4:!!!!!}……|
^………………………… |
|
]]}
+ feed('<C-E>')
+ screen:expect{grid=[[
+ {1:<<<}………………^… |
+ {1:~ }|
+ |
+ ]]}
+ screen:try_resize(40, 3)
+ screen:expect{grid=[[
+ {1:<<<}{4:!!!!!}……………………………^… |
+ {1:~ }|
+ |
+ ]]}
+ feed('<C-Y>')
+ screen:expect{grid=[[
+ {4:?????}……………………………………………………………………………………………|
+ ………{4:!!!!!}……………………………^… |
+ |
+ ]]}
end)
it('can have virtual text of overlay position and styling', function()
@@ -1338,7 +1378,56 @@ describe('extmark decorations', function()
screen:expect_unchanged(true)
end)
- it('highlights the beginning of a TAB char correctly', function()
+ it('highlight is combined with syntax and sign linehl #20004', function()
+ screen:try_resize(50, 3)
+ insert([[
+ function Func()
+ end]])
+ feed('gg')
+ command('set ft=lua')
+ command('syntax on')
+ meths.buf_set_extmark(0, ns, 0, 0, { end_col = 3, hl_mode = 'combine', hl_group = 'Visual' })
+ command('hi default MyLine gui=underline')
+ command('sign define CurrentLine linehl=MyLine')
+ funcs.sign_place(6, 'Test', 'CurrentLine', '', { lnum = 1 })
+ screen:expect{grid=[[
+ {30:^fun}{31:ction}{32: Func() }|
+ {6:end} |
+ |
+ ]]}
+ end)
+
+ it('highlight works after TAB with sidescroll #14201', function()
+ screen:try_resize(50, 3)
+ command('set nowrap')
+ meths.buf_set_lines(0, 0, -1, true, {'\tword word word word'})
+ meths.buf_set_extmark(0, ns, 0, 1, { end_col = 3, hl_group = 'ErrorMsg' })
+ screen:expect{grid=[[
+ ^ {4:wo}rd word word word |
+ {1:~ }|
+ |
+ ]]}
+ feed('7zl')
+ screen:expect{grid=[[
+ {4:^wo}rd word word word |
+ {1:~ }|
+ |
+ ]]}
+ feed('zl')
+ screen:expect{grid=[[
+ {4:^wo}rd word word word |
+ {1:~ }|
+ |
+ ]]}
+ feed('zl')
+ screen:expect{grid=[[
+ {4:^o}rd word word word |
+ {1:~ }|
+ |
+ ]]}
+ end)
+
+ it('highlights the beginning of a TAB char correctly #23734', function()
screen:try_resize(50, 3)
meths.buf_set_lines(0, 0, -1, true, {'this is the\ttab'})
meths.buf_set_extmark(0, ns, 0, 11, { end_col = 15, hl_group = 'ErrorMsg' })
@@ -1357,7 +1446,20 @@ describe('extmark decorations', function()
]]}
end)
- pending('highlight applies to a full Tab in visual block mode #23734', function()
+ it('highlight applies to a full TAB on line with matches #20885', function()
+ screen:try_resize(50, 3)
+ meths.buf_set_lines(0, 0, -1, true, {'\t-- match1', ' -- match2'})
+ funcs.matchadd('Underlined', 'match')
+ meths.buf_set_extmark(0, ns, 0, 0, { end_row = 1, end_col = 0, hl_group = 'Visual' })
+ meths.buf_set_extmark(0, ns, 1, 0, { end_row = 2, end_col = 0, hl_group = 'Visual' })
+ screen:expect{grid=[[
+ {18: ^ -- }{29:match}{18:1} |
+ {18: -- }{29:match}{18:2} |
+ |
+ ]]}
+ end)
+
+ pending('highlight applies to a full TAB in visual block mode', function()
screen:try_resize(50, 8)
meths.buf_set_lines(0, 0, -1, true, {'asdf', '\tasdf', '\tasdf', '\tasdf', 'asdf'})
meths.buf_set_extmark(0, ns, 0, 0, {end_row = 5, end_col = 0, hl_group = 'Underlined'})
@@ -2125,34 +2227,55 @@ bbbbbbb]])
end)
it('does not crash at column 0 when folded in a wide window', function()
- screen:try_resize(82, 4)
+ screen:try_resize(82, 5)
command('hi! CursorLine guibg=NONE guifg=Red gui=NONE')
command('set cursorline')
insert([[
aaaaa
bbbbb
+
ccccc]])
meths.buf_set_extmark(0, ns, 0, 0, { virt_text = {{'foo'}}, virt_text_pos = 'inline' })
+ meths.buf_set_extmark(0, ns, 2, 0, { virt_text = {{'bar'}}, virt_text_pos = 'inline' })
screen:expect{grid=[[
fooaaaaa |
bbbbb |
+ bar |
{16:cccc^c }|
|
]]}
command('1,2fold')
screen:expect{grid=[[
{17:+-- 2 lines: aaaaa·······························································}|
+ bar |
{16:cccc^c }|
{1:~ }|
|
]]}
- feed('k')
+ feed('2k')
screen:expect{grid=[[
{18:^+-- 2 lines: aaaaa·······························································}|
+ bar |
ccccc |
{1:~ }|
|
]]}
+ command('3,4fold')
+ screen:expect{grid=[[
+ {18:^+-- 2 lines: aaaaa·······························································}|
+ {17:+-- 2 lines: ccccc·······························································}|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+ feed('j')
+ screen:expect{grid=[[
+ {17:+-- 2 lines: aaaaa·······························································}|
+ {18:^+-- 2 lines: ccccc·······························································}|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
end)
end)
@@ -2164,7 +2287,7 @@ describe('decorations: virtual lines', function()
screen:attach()
screen:set_default_attr_ids {
[1] = {bold=true, foreground=Screen.colors.Blue};
- [2] = {foreground = Screen.colors.Cyan4};
+ [2] = {foreground = Screen.colors.DarkCyan};
[3] = {background = Screen.colors.Yellow1};
[4] = {bold = true};
[5] = {background = Screen.colors.Yellow, foreground = Screen.colors.Blue};
@@ -2883,6 +3006,30 @@ if (h->n_buckets < new_n_buckets) { // expand
]]}
end)
+ it('does not show twice if end_row or end_col is specified #18622', function()
+ insert([[
+ aaa
+ bbb
+ ccc
+ ddd]])
+ meths.buf_set_extmark(0, ns, 0, 0, {end_row = 2, virt_lines = {{{'VIRT LINE 1', 'NonText'}}}})
+ meths.buf_set_extmark(0, ns, 3, 0, {end_col = 2, virt_lines = {{{'VIRT LINE 2', 'NonText'}}}})
+ screen:expect{grid=[[
+ aaa |
+ {1:VIRT LINE 1} |
+ bbb |
+ ccc |
+ dd^d |
+ {1:VIRT LINE 2} |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+ end)
+
end)
describe('decorations: signs', function()
@@ -3195,8 +3342,8 @@ l5
insert(example_test3)
feed 'gg'
- helpers.command('sign define Oldsign text=O3')
- helpers.command([[exe 'sign place 42 line=1 name=Oldsign priority=10 buffer=' . bufnr('')]])
+ command('sign define Oldsign text=O3')
+ command([[exe 'sign place 42 line=1 name=Oldsign priority=10 buffer=' . bufnr('')]])
meths.buf_set_extmark(0, ns, 0, -1, {sign_text='S4', priority=100})
meths.buf_set_extmark(0, ns, 0, -1, {sign_text='S2', priority=5})
@@ -3219,6 +3366,39 @@ l5
]]}
end)
+ it('does not overflow with many old signs #23852', function()
+ screen:try_resize(20, 3)
+
+ command('set signcolumn:auto:9')
+ command('sign define Oldsign text=O3')
+ command([[exe 'sign place 01 line=1 name=Oldsign priority=10 buffer=' . bufnr('')]])
+ command([[exe 'sign place 02 line=1 name=Oldsign priority=10 buffer=' . bufnr('')]])
+ command([[exe 'sign place 03 line=1 name=Oldsign priority=10 buffer=' . bufnr('')]])
+ command([[exe 'sign place 04 line=1 name=Oldsign priority=10 buffer=' . bufnr('')]])
+ command([[exe 'sign place 05 line=1 name=Oldsign priority=10 buffer=' . bufnr('')]])
+ command([[exe 'sign place 06 line=1 name=Oldsign priority=10 buffer=' . bufnr('')]])
+ command([[exe 'sign place 07 line=1 name=Oldsign priority=10 buffer=' . bufnr('')]])
+ command([[exe 'sign place 08 line=1 name=Oldsign priority=10 buffer=' . bufnr('')]])
+ command([[exe 'sign place 09 line=1 name=Oldsign priority=10 buffer=' . bufnr('')]])
+ screen:expect{grid=[[
+ O3O3O3O3O3O3O3O3O3^ |
+ {2:~ }|
+ |
+ ]]}
+
+ meths.buf_set_extmark(0, ns, 0, -1, {sign_text='S1', priority=1})
+ screen:expect_unchanged()
+
+ meths.buf_set_extmark(0, ns, 0, -1, {sign_text='S5', priority=200})
+ screen:expect{grid=[[
+ O3O3O3O3O3O3O3O3S5^ |
+ {2:~ }|
+ |
+ ]]}
+
+ assert_alive()
+ end)
+
it('does not set signcolumn for signs without text', function()
screen:try_resize(20, 3)
meths.set_option_value('signcolumn', 'auto', {})
diff --git a/test/functional/ui/highlight_spec.lua b/test/functional/ui/highlight_spec.lua
index 9a4be4573c..c68f4cf34c 100644
--- a/test/functional/ui/highlight_spec.lua
+++ b/test/functional/ui/highlight_spec.lua
@@ -426,7 +426,7 @@ describe('highlight', function()
^ |
{2:~ }|
|
- ]],{
+ ]], {
[1] = {strikethrough = true},
[2] = {bold = true, foreground = Screen.colors.Blue1},
})
@@ -515,7 +515,7 @@ describe('highlight', function()
{1:neovim} tabbed^ |
{0:~ }|
{5:-- INSERT --} |
- ]],{
+ ]], {
[0] = {bold=true, foreground=Screen.colors.Blue},
[1] = {background = Screen.colors.Yellow, foreground = Screen.colors.Red,
special = Screen.colors.Red},
@@ -527,7 +527,7 @@ describe('highlight', function()
end)
- it("'diff', syntax and extmark", function()
+ it("'diff', syntax and extmark #23722", function()
local screen = Screen.new(25,10)
screen:attach()
exec([[
@@ -549,7 +549,7 @@ describe('highlight', function()
{4:~ }|
{8:[No Name] }|
|
- ]],{
+ ]], {
[0] = {Screen.colors.WebGray, foreground = Screen.colors.DarkBlue},
[1] = {background = Screen.colors.Grey, foreground = Screen.colors.Blue4},
[2] = {foreground = Screen.colors.Red, background = Screen.colors.LightBlue},
diff --git a/test/functional/ui/inccommand_user_spec.lua b/test/functional/ui/inccommand_user_spec.lua
index 6329ece40a..9cc6e095c5 100644
--- a/test/functional/ui/inccommand_user_spec.lua
+++ b/test/functional/ui/inccommand_user_spec.lua
@@ -401,6 +401,40 @@ describe("'inccommand' for user commands", function()
feed('e')
assert_alive()
end)
+
+ it('no crash when adding highlight after :substitute #21495', function()
+ command('set inccommand=nosplit')
+ exec_lua([[
+ vim.api.nvim_create_user_command("Crash", function() end, {
+ preview = function(_, preview_ns, _)
+ vim.cmd("%s/text/cats/g")
+ vim.api.nvim_buf_add_highlight(0, preview_ns, "Search", 0, 0, -1)
+ return 1
+ end,
+ })
+ ]])
+ feed(':C')
+ screen:expect([[
+ {1: cats on line 1} |
+ more cats on line 2 |
+ oh no, even more cats |
+ will the cats ever stop |
+ oh well |
+ did the cats stop |
+ why won't it stop |
+ make the cats stop |
+ |
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ :C^ |
+ ]])
+ assert_alive()
+ end)
end)
describe("'inccommand' with multiple buffers", function()
diff --git a/test/functional/ui/searchhl_spec.lua b/test/functional/ui/searchhl_spec.lua
index 3c8dceb8cb..1e42689200 100644
--- a/test/functional/ui/searchhl_spec.lua
+++ b/test/functional/ui/searchhl_spec.lua
@@ -512,6 +512,36 @@ describe('search highlighting', function()
{1:~ }│{1:~ }|
/file^ |
]])
+ feed('<Esc>')
+
+ command('set rtp^=test/functional/fixtures')
+ -- incsearch works after c_CTRL-R inserts clipboard register
+
+ command('let @* = "first"')
+ feed('/<C-R>*')
+ screen:expect([[
+ the {3:first} line │the {2:first} line |
+ in a little file │in a little file |
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ /first^ |
+ ]])
+ feed('<Esc>')
+
+ command('let @+ = "little"')
+ feed('/<C-R>+')
+ screen:expect([[
+ the first line │the first line |
+ in a {3:little} file │in a {2:little} file |
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ /little^ |
+ ]])
+ feed('<Esc>')
end)
it('works with incsearch and offset', function()
diff --git a/test/functional/ui/statuscolumn_spec.lua b/test/functional/ui/statuscolumn_spec.lua
index c218bd8fd6..6624fb008d 100644
--- a/test/functional/ui/statuscolumn_spec.lua
+++ b/test/functional/ui/statuscolumn_spec.lua
@@ -424,6 +424,21 @@ describe('statuscolumn', function()
]])
end)
+ it('does not corrupt the screen with minwid sign item', function()
+ screen:try_resize(screen._width, 3)
+ screen:set_default_attr_ids({
+ [0] = {foreground = Screen.colors.Brown},
+ [1] = {foreground = Screen.colors.Blue4, background = Screen.colors.Gray},
+ })
+ command([[set stc=%6s\ %l]])
+ exec_lua('vim.api.nvim_buf_set_extmark(0, ns, 7, 0, {sign_text = "𒀀"})')
+ screen:expect([[
+ {0: 𒀀 8}^aaaaa |
+ {0: }{1: }{0: 9}aaaaa |
+ |
+ ]])
+ end)
+
for _, model in ipairs(mousemodels) do
it("works with 'statuscolumn' clicks with mousemodel=" .. model, function()
command('set mousemodel=' .. model)
diff --git a/test/functional/vimscript/api_functions_spec.lua b/test/functional/vimscript/api_functions_spec.lua
index 3404b06a55..14678a966d 100644
--- a/test/functional/vimscript/api_functions_spec.lua
+++ b/test/functional/vimscript/api_functions_spec.lua
@@ -133,7 +133,7 @@ describe('eval-API', function()
})
command("set ft=vim")
- command("let &rtp='build/runtime/,'.&rtp")
+ command("set rtp^=build/runtime/")
command("syntax on")
insert([[
call bufnr('%')
diff --git a/test/old/testdir/test_filetype.vim b/test/old/testdir/test_filetype.vim
index 2858bf1add..9ca267f78c 100644
--- a/test/old/testdir/test_filetype.vim
+++ b/test/old/testdir/test_filetype.vim
@@ -126,6 +126,7 @@ let s:filename_checks = {
\ 'confini': ['/etc/pacman.conf', 'any/etc/pacman.conf', 'mpv.conf', 'any/.aws/config', 'any/.aws/credentials', 'file.nmconnection'],
\ 'context': ['tex/context/any/file.tex', 'file.mkii', 'file.mkiv', 'file.mkvi', 'file.mkxl', 'file.mklx'],
\ 'cook': ['file.cook'],
+ \ 'corn': ['file.corn'],
\ 'cpon': ['file.cpon'],
\ 'cpp': ['file.cxx', 'file.c++', 'file.hh', 'file.hxx', 'file.hpp', 'file.ipp', 'file.moc', 'file.tcc', 'file.inl', 'file.tlh', 'file.cppm', 'file.ccm', 'file.cxxm', 'file.c++m'],
\ 'cqlang': ['file.cql'],
@@ -370,7 +371,7 @@ let s:filename_checks = {
\ 'file.wxm', 'maxima-init.mac'],
\ 'mel': ['file.mel'],
\ 'mermaid': ['file.mmd', 'file.mmdc', 'file.mermaid'],
- \ 'meson': ['meson.build', 'meson_options.txt'],
+ \ 'meson': ['meson.build', 'meson.options', 'meson_options.txt'],
\ 'messages': ['/log/auth', '/log/cron', '/log/daemon', '/log/debug', '/log/kern', '/log/lpr', '/log/mail', '/log/messages', '/log/news/news', '/log/syslog', '/log/user',
\ '/log/auth.log', '/log/cron.log', '/log/daemon.log', '/log/debug.log', '/log/kern.log', '/log/lpr.log', '/log/mail.log', '/log/messages.log', '/log/news/news.log', '/log/syslog.log', '/log/user.log',
\ '/log/auth.err', '/log/cron.err', '/log/daemon.err', '/log/debug.err', '/log/kern.err', '/log/lpr.err', '/log/mail.err', '/log/messages.err', '/log/news/news.err', '/log/syslog.err', '/log/user.err',
@@ -1449,6 +1450,12 @@ func Test_mod_file()
bwipe!
call delete('go.mod')
+ call writefile(['module M'], 'go.mod')
+ split go.mod
+ call assert_equal('gomod', &filetype)
+ bwipe!
+ call delete('go.mod')
+
filetype off
endfunc
diff --git a/test/old/testdir/test_hlsearch.vim b/test/old/testdir/test_hlsearch.vim
index 043d378a39..fb1695220a 100644
--- a/test/old/testdir/test_hlsearch.vim
+++ b/test/old/testdir/test_hlsearch.vim
@@ -85,4 +85,23 @@ func Test_hlsearch_Ctrl_R()
call StopVimInTerminal(buf)
endfunc
+func Test_hlsearch_clipboard()
+ CheckRunVimInTerminal
+ CheckFeature clipboard_working
+
+ let lines =<< trim END
+ set incsearch hlsearch
+ let @* = "text"
+ put *
+ END
+ call writefile(lines, 'XhlsearchClipboard', 'D')
+ let buf = RunVimInTerminal('-S XhlsearchClipboard', #{rows: 6, cols: 60})
+
+ call term_sendkeys(buf, "/\<C-R>*")
+ call VerifyScreenDump(buf, 'Test_hlsearch_ctrlr_1', {})
+
+ call term_sendkeys(buf, "\<Esc>")
+ call StopVimInTerminal(buf)
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/test/old/testdir/test_registers.vim b/test/old/testdir/test_registers.vim
index bbf1aa53b5..70dac535b4 100644
--- a/test/old/testdir/test_registers.vim
+++ b/test/old/testdir/test_registers.vim
@@ -55,8 +55,9 @@ func Test_display_registers()
call feedkeys("i\<C-R>=2*4\n\<esc>")
call feedkeys(":ls\n", 'xt')
- let a = execute('display')
- let b = execute('registers')
+ " these commands work in the sandbox
+ let a = execute('sandbox display')
+ let b = execute('sandbox registers')
call assert_equal(a, b)
call assert_match('^\nType Name Content\n'