aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.clang-format1
-rw-r--r--runtime/doc/api.txt2
-rw-r--r--runtime/doc/dev_style.txt44
-rw-r--r--runtime/doc/starting.txt2
-rw-r--r--runtime/lua/vim/filetype.lua178
-rw-r--r--runtime/lua/vim/filetype/detect.lua71
-rw-r--r--runtime/lua/vim/lsp/handlers.lua2
-rw-r--r--src/nvim/README.md11
-rw-r--r--src/nvim/api/private/dispatch.c33
-rw-r--r--src/nvim/api/private/dispatch.h1
-rw-r--r--src/nvim/api/vim.c2
-rw-r--r--src/nvim/channel.c2
-rw-r--r--src/nvim/eval.c7
-rw-r--r--src/nvim/ex_cmds.c6
-rw-r--r--src/nvim/generators/gen_api_dispatch.lua39
-rwxr-xr-xsrc/nvim/generators/gen_api_ui_events.lua32
-rw-r--r--src/nvim/globals.h5
-rw-r--r--src/nvim/log.c66
-rw-r--r--src/nvim/log.h47
-rw-r--r--src/nvim/main.c1
-rw-r--r--src/nvim/map.c4
-rw-r--r--src/nvim/map.h3
-rw-r--r--src/nvim/msgpack_rpc/channel.c14
-rw-r--r--src/nvim/msgpack_rpc/channel_defs.h1
-rw-r--r--src/nvim/regexp.c94
-rw-r--r--src/nvim/regexp_defs.h5
-rw-r--r--src/nvim/state.c4
-rw-r--r--src/nvim/testdir/test_filetype_lua.vim1
-rw-r--r--src/nvim/ui.c6
-rw-r--r--src/nvim/ui_client.c27
-rw-r--r--src/nvim/ui_client.h5
-rw-r--r--test/functional/core/startup_spec.lua1
-rw-r--r--third-party/CMakeLists.txt4
33 files changed, 490 insertions, 231 deletions
diff --git a/.clang-format b/.clang-format
index afb0df2e25..a31d753217 100644
--- a/.clang-format
+++ b/.clang-format
@@ -48,6 +48,7 @@ IncludeCategories:
AlignConsecutiveMacros: AcrossEmptyLines
IndentPPDirectives: AfterHash
SpaceBeforeParens: ControlStatementsExceptControlMacros
+PPIndentWidth: 1
ForEachMacros:
- FOR_ALL_AUEVENTS
- FOR_ALL_AUPATS_IN_EVENT
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt
index 6c8c35486e..4dbb3a9a30 100644
--- a/runtime/doc/api.txt
+++ b/runtime/doc/api.txt
@@ -1708,7 +1708,7 @@ nvim_set_vvar({name}, {value}) *nvim_set_vvar()*
nvim_strwidth({text}) *nvim_strwidth()*
Calculates the number of display cells occupied by `text`.
- <Tab> counts as one cell.
+ Control characters including <Tab> count as one cell.
Parameters: ~
{text} Some text
diff --git a/runtime/doc/dev_style.txt b/runtime/doc/dev_style.txt
index a2ea1204b5..ac4f489bc9 100644
--- a/runtime/doc/dev_style.txt
+++ b/runtime/doc/dev_style.txt
@@ -789,11 +789,6 @@ example, `"\uFEFF"`, is the Unicode zero-width no-break space character, which
would be invisible if included in the source as straight UTF-8.
-Spaces vs. Tabs ~
-
-Use only spaces, and indent 2 spaces at a time. Do not use tabs in your code.
-
-
Function Declarations and Definitions ~
Return type on the same line as function name, parameters on the same line if
@@ -903,7 +898,7 @@ no name, assume a zero-length name. >
Conditionals ~
-Don't use spaces inside parentheses. Always use curly braces. >
+Don't use spaces inside parentheses. >
if (condition) { // no spaces inside parentheses
... // 2 space indent.
@@ -923,8 +918,8 @@ warn you if any values are not handled). If the default case should never
execute, simply `assert`: >
switch (var) {
- case 0: // 2 space indent
- ... // 4 space indent
+ case 0:
+ ...
break;
case 1:
...
@@ -951,15 +946,6 @@ Note that:
- There are no spaces around the period or arrow when accessing a member.
- Pointer operators have no space after the * or &.
-When declaring a pointer variable or argument, place the asterisk adjacent to
-the variable name: >
-
- char *c;
-
- char * c; // BAD: spaces on both sides of *
- char* c; // BAD
-
-
Boolean Expressions ~
When you have a boolean expression that is longer than the standard line
@@ -991,15 +977,10 @@ expr;`. >
Horizontal Whitespace ~
-Use of horizontal whitespace depends on location. Never put trailing
-whitespace at the end of a line.
+Use of horizontal whitespace depends on location.
General ~
>
- if (x) { // Open braces should always have a space before them.
- ...
- }
- int i = 0; // Semicolons usually have no space before them.
int x[] = { 0 }; // Spaces inside braces for braced-init-list.
<
@@ -1019,21 +1000,6 @@ whitespace at the end of a line.
};
<
- Loops and Conditionals ~
->
- if (b) { // Space after the keyword in condition.
- } else { // Spaces around else.
- }
- while (test) {} // There is usually no space inside parentheses.
- for (; i < 5; i++) { // For loops always have a space after the
- ... // semicolon and no a space before the
- ... // semicolon.
- }
- switch (i) {
- case 1: // No space before colon in a switch case.
- ...
- case 2: break; // Space after a colon if there's code after it.
-<
Operators ~
>
@@ -1043,8 +1009,6 @@ whitespace at the end of a line.
x++; // arguments.
if (x && !y)
...
- v = w*x + y/z; // Use spaces to indicate operator precedence.
- v = w * (x + z); // Parentheses should have no spaces inside them.
i = (int)d; // No spaces after a cast operator.
<
diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt
index 8b88fa9363..4a88939715 100644
--- a/runtime/doc/starting.txt
+++ b/runtime/doc/starting.txt
@@ -1349,7 +1349,7 @@ STATE DIRECTORY (DEFAULT) ~
Note: Throughout the user manual these defaults are used as placeholders, e.g.
"~/.config" is understood to mean "$XDG_CONFIG_HOME or ~/.config".
-LOG FILE *$NVIM_LOG_FILE*
+LOG FILE *$NVIM_LOG_FILE* *E5430*
Besides 'debug' and 'verbose', Nvim keeps a general log file for internal
debugging, plugins and RPC clients. >
:echo $NVIM_LOG_FILE
diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
index c26a43f776..7a8f7187b9 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -23,12 +23,15 @@ local function starsetf(ft)
end
---@private
-local function getline(bufnr, lnum)
- return api.nvim_buf_get_lines(bufnr, lnum - 1, lnum, false)[1] or ''
+local function getline(bufnr, start_lnum, end_lnum)
+ end_lnum = end_lnum or start_lnum
+ local lines = vim.api.nvim_buf_get_lines(bufnr, start_lnum - 1, end_lnum, false)
+ return table.concat(lines) or ''
end
-- Filetypes based on file extension
-- luacheck: push no unused args
+---@diagnostic disable: unused-local
local extension = {
-- BEGIN EXTENSION
['8th'] = '8th',
@@ -52,8 +55,17 @@ local extension = {
art = 'art',
asciidoc = 'asciidoc',
adoc = 'asciidoc',
+ asa = function(path, bufnr)
+ if vim.g.filetype_asa then
+ return vim.g.filetype_asa
+ end
+ return 'aspvbs'
+ end,
['asn1'] = 'asn',
asn = 'asn',
+ asp = function(path, bufnr)
+ return require('vim.filetype.detect').asp(bufnr)
+ end,
atl = 'atlas',
as = 'atlas',
ahk = 'autohotkey',
@@ -92,7 +104,6 @@ local extension = {
cho = 'chordpro',
chordpro = 'chordpro',
eni = 'cl',
- dcl = 'clean',
icl = 'clean',
cljx = 'clojure',
clj = 'clojure',
@@ -129,7 +140,7 @@ local extension = {
hxx = 'cpp',
hpp = 'cpp',
cpp = function(path, bufnr)
- if vim.g.cynlib_syntax_for_cc then
+ if vim.g.cynlib_syntax_for_cpp then
return 'cynlib'
end
return 'cpp'
@@ -688,6 +699,16 @@ local extension = {
bbl = 'tex',
latex = 'tex',
sty = 'tex',
+ cls = function(path, bufnr)
+ local line = getline(bufnr, 1)
+ if line:find('^%%') then
+ return 'tex'
+ elseif line:find('^#') and line:lower():find('rexx') then
+ return 'rexx'
+ else
+ return 'st'
+ end
+ end,
texi = 'texinfo',
txi = 'texinfo',
texinfo = 'texinfo',
@@ -766,6 +787,12 @@ local extension = {
csproj = 'xml',
wpl = 'xml',
xmi = 'xml',
+ xpm = function(path, bufnr)
+ if getline(bufnr, 1):find('XPM2') then
+ return 'xpm2'
+ end
+ return 'xpm'
+ end,
['xpm2'] = 'xpm2',
xqy = 'xquery',
xqm = 'xquery',
@@ -1009,6 +1036,145 @@ local extension = {
return 'text'
end
end,
+ cmd = function(path, bufnr)
+ if getline(bufnr, 1):find('^/%*') then
+ return 'rexx'
+ end
+ return 'dosbatch'
+ end,
+ rul = function(path, bufnr)
+ return require('vim.filetype.detect').rul(bufnr)
+ end,
+ cpy = function(path, bufnr)
+ if getline(bufnr, 1):find('^##') then
+ return 'python'
+ end
+ return 'cobol'
+ end,
+ dsl = function(path, bufnr)
+ if getline(bufnr, 1):find('^%s*<!') then
+ return 'dsl'
+ end
+ return 'structurizr'
+ end,
+ edf = 'edif',
+ edfi = 'edif',
+ edo = 'edif',
+ edn = function(path, bufnr)
+ return require('vim.filetype.detect').edn(bufnr)
+ end,
+ smil = function(path, bufnr)
+ if getline(bufnr, 1):find('<%?%s*xml.*%?>') then
+ return 'xml'
+ end
+ return 'smil'
+ end,
+ smi = function(path, bufnr)
+ return require('vim.filetype.detect').smi(bufnr)
+ end,
+ install = function(path, bufnr)
+ if getline(bufnr, 1):lower():find('<%?php') then
+ return 'php'
+ end
+ return require('vim.filetype.detect').sh(path, bufnr, 'bash')
+ end,
+ pm = function(path, bufnr)
+ local line = getline(bufnr, 1)
+ if line:find('XPM2') then
+ return 'xpm2'
+ elseif line:find('XPM') then
+ return 'xpm'
+ else
+ return 'perl'
+ end
+ end,
+ me = function(path, bufnr)
+ local filename = vim.fn.fnamemodify(path, ':t'):lower()
+ if filename ~= 'read.me' and filename ~= 'click.me' then
+ return 'nroff'
+ end
+ end,
+ reg = function(path, bufnr)
+ local line = getline(bufnr, 1):lower()
+ if line:find('^regedit[0-9]*%s*$') or line:find('^windows registry editor version %d*%.%d*%s*$') then
+ return 'registry'
+ end
+ end,
+ decl = function(path, bufnr)
+ return require('vim.filetype.detect').decl(bufnr)
+ end,
+ dec = function(path, bufnr)
+ return require('vim.filetype.detect').decl(bufnr)
+ end,
+ dcl = function(path, bufnr)
+ local decl = require('vim.filetype.detect').decl(bufnr)
+ if decl then
+ return decl
+ end
+ return 'clean'
+ end,
+ web = function(path, bufnr)
+ return require('vim.filetype.detect').web(bufnr)
+ end,
+ ttl = function(path, bufnr)
+ local line = getline(bufnr, 1):lower()
+ if line:find('^@?prefix') or line:find('^@?base') then
+ return 'turtle'
+ end
+ return 'teraterm'
+ end,
+ am = function(path, bufnr)
+ if not path:lower():find('makefile%.am$') then
+ return 'elf'
+ end
+ end,
+ ['m4'] = function(path, bufnr)
+ local path_lower = path:lower()
+ if not path_lower:find('html%.m4$') and not path_lower:find('fvwm2rc') then
+ return 'm4'
+ end
+ end,
+ hw = function(path, bufnr)
+ return require('vim.filetype.detect').hw(bufnr)
+ end,
+ module = function(path, bufnr)
+ return require('vim.filetype.detect').hw(bufnr)
+ end,
+ pkg = function(path, bufnr)
+ return require('vim.filetype.detect').hw(bufnr)
+ end,
+ ms = function(path, bufnr)
+ if not require('vim.filetype.detect').nroff(bufnr) then
+ return 'xmath'
+ end
+ end,
+ t = function(path, bufnr)
+ if not require('vim.filetype.detect').nroff(bufnr) and not require('vim.filetype.detect').perl(path, bufnr) then
+ return 'tads'
+ end
+ end,
+ class = function(path, bufnr)
+ -- Check if not a Java class (starts with '\xca\xfe\xba\xbe')
+ if not getline(bufnr, 1):find('^\202\254\186\190') then
+ return 'stata'
+ end
+ end,
+ sgml = function(path, bufnr)
+ return require('vim.filetype.detect').sgml(bufnr)
+ end,
+ sgm = function(path, bufnr)
+ return require('vim.filetype.detect').sgml(bufnr)
+ end,
+ rc = function(path, bufnr)
+ if not path:find('/etc/Muttrc%.d/') then
+ return 'rc'
+ end
+ end,
+ rch = function(path, bufnr)
+ if not path:find('/etc/Muttrc%.d/') then
+ return 'rc'
+ end
+ end,
-- END EXTENSION
}
@@ -1421,6 +1587,7 @@ local pattern = {
['.*/boot/grub/grub%.conf'] = 'grub',
['.*/boot/grub/menu%.lst'] = 'grub',
['.*/etc/grub%.conf'] = 'grub',
+ [vim.env.VIMRUNTIME .. '/doc/.*%.txt'] = 'help',
['hg%-editor%-.*%.txt'] = 'hgcommit',
['.*/etc/host%.conf'] = 'hostconf',
['.*/etc/hosts%.deny'] = 'hostsaccess',
@@ -1475,6 +1642,7 @@ local pattern = {
['.*/etc/passwd'] = 'passwd',
['.*/etc/passwd%.edit'] = 'passwd',
['.*/etc/shadow-'] = 'passwd',
+ ['.*%.php%d'] = 'php',
['.*/%.pinforc'] = 'pinfo',
['.*/etc/pinforc'] = 'pinfo',
['.*/etc/protocols'] = 'protocols',
@@ -1707,6 +1875,8 @@ local pattern = {
['.*%.[Ss][Yy][Ss]'] = function(path, bufnr)
return require('vim.filetype.detect').sys(bufnr)
end,
+ ['%.?gitolite%.rc'] = 'perl',
+ ['example%.gitolite%.rc'] = 'perl',
-- Neovim only
['.*/queries/.*%.scm'] = 'query', -- tree-sitter queries
-- END PATTERN
diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua
index f195693dcf..b35303eefc 100644
--- a/runtime/lua/vim/filetype/detect.lua
+++ b/runtime/lua/vim/filetype/detect.lua
@@ -983,6 +983,77 @@ function M.y(bufnr)
return 'yacc'
end
+function M.decl(bufnr)
+ for _, line in ipairs(getlines(bufnr, 1, 3)) do
+ if line:lower():find('^<!sgml') then
+ return 'sgmldecl'
+ end
+ end
+end
+
+function M.sgml(bufnr)
+ local lines = table.concat(getlines(bufnr, 1, 5))
+ if lines:find('linuxdoc') then
+ return 'smgllnx'
+ elseif lines:find('<!DOCTYPE.*DocBook') then
+ vim.b[bufnr].docbk_type = 'sgml'
+ vim.b[bufnr].docbk_ver = 4
+ return 'docbk'
+ else
+ return 'sgml'
+ end
+end
+
+function M.web(bufnr)
+ for _, line in ipairs(getlines(bufnr, 1, 5)) do
+ if line:find('^%%') then
+ return 'web'
+ end
+ end
+ return 'winbatch'
+end
+
+function M.rul(bufnr)
+ if table.concat(getlines(bufnr, 1, 6)):lower():find('installshield') then
+ return 'ishd'
+ end
+ return 'diva'
+end
+
+function M.asp(bufnr)
+ if vim.g.filetype_asp then
+ return vim.g.filetype_asp
+ elseif table.concat(getlines(bufnr, 1, 3)):lower():find('perlscript') then
+ return 'aspperl'
+ else
+ return 'aspvbs'
+ end
+end
+
+function M.edn(bufnr)
+ local line = getlines(bufnr, 1)
+ if matchregex(line, [[\c^\s*(\s*edif\>]]) then
+ return 'edif'
+ else
+ return 'clojure'
+ end
+end
+
+function M.smi(bufnr)
+ local line = getlines(bufnr, 1)
+ if matchregex(line, [[\c\<smil\>]]) then
+ return 'smil'
+ else
+ return 'mib'
+ end
+end
+
+function M.hw(bufnr)
+ if getlines(bufnr, 1):lower():find('<%?php') then
+ return 'php'
+ end
+ return 'virata'
+end
-- luacheck: pop
-- luacheck: pop
diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua
index 61cc89dcac..935f4b64f8 100644
--- a/runtime/lua/vim/lsp/handlers.lua
+++ b/runtime/lua/vim/lsp/handlers.lua
@@ -40,10 +40,12 @@ local function progress_handler(_, result, ctx, _)
if val.kind == 'begin' then
client.messages.progress[token] = {
title = val.title,
+ cancellable = val.cancellable,
message = val.message,
percentage = val.percentage,
}
elseif val.kind == 'report' then
+ client.messages.progress[token].cancellable = val.cancellable
client.messages.progress[token].message = val.message
client.messages.progress[token].percentage = val.percentage
elseif val.kind == 'end' then
diff --git a/src/nvim/README.md b/src/nvim/README.md
index 9417629691..91fb3ca2f6 100644
--- a/src/nvim/README.md
+++ b/src/nvim/README.md
@@ -23,7 +23,7 @@ Logs
Low-level log messages sink to `$NVIM_LOG_FILE`.
-UI events are logged at DEBUG level (`DEBUG_LOG_LEVEL`).
+UI events are logged at DEBUG level (`LOGLVL_DBG`).
rm -rf build/
make CMAKE_EXTRA_FLAGS="-DMIN_LOG_LEVEL=0"
@@ -204,9 +204,14 @@ Then you can compare `bar` with another session, to debug TUI behavior.
### TUI redraw
-Set the 'writedelay' option to see where and when the UI is painted.
+Set the 'writedelay' and 'redrawdebug' options to see where and when the UI is painted.
- :set writedelay=1
+ :set writedelay=50 rdb=compositor
+
+Note: neovim uses an internal screenbuffer to only send minimal updates even if a large
+region is repainted internally. To also highlight excess internal redraws, use
+
+ :set writedelay=50 rdb=compositor,nodelta
### Terminal reference
diff --git a/src/nvim/api/private/dispatch.c b/src/nvim/api/private/dispatch.c
index ba2e560d63..3da2c2cde4 100644
--- a/src/nvim/api/private/dispatch.c
+++ b/src/nvim/api/private/dispatch.c
@@ -32,37 +32,22 @@
#include "nvim/api/window.h"
#include "nvim/ui_client.h"
-static Map(String, MsgpackRpcRequestHandler) methods = MAP_INIT;
-
-static void msgpack_rpc_add_method_handler(String method, MsgpackRpcRequestHandler handler)
-{
- map_put(String, MsgpackRpcRequestHandler)(&methods, method, handler);
-}
-
-void msgpack_rpc_add_redraw(void)
-{
- msgpack_rpc_add_method_handler(STATIC_CSTR_AS_STRING("redraw"),
- (MsgpackRpcRequestHandler) { .fn = ui_client_handle_redraw,
- .fast = true });
-}
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "api/private/dispatch_wrappers.generated.h"
+#endif
/// @param name API method name
/// @param name_len name size (includes terminating NUL)
MsgpackRpcRequestHandler msgpack_rpc_get_handler_for(const char *name, size_t name_len,
Error *error)
{
- String m = { .data = (char *)name, .size = name_len };
- MsgpackRpcRequestHandler rv =
- map_get(String, MsgpackRpcRequestHandler)(&methods, m);
+ int hash = msgpack_rpc_get_handler_for_hash(name, name_len);
- if (!rv.fn) {
+ if (hash < 0) {
api_set_error(error, kErrorTypeException, "Invalid method: %.*s",
- m.size > 0 ? (int)m.size : (int)sizeof("<empty>"),
- m.size > 0 ? m.data : "<empty>");
+ name_len > 0 ? (int)name_len : (int)sizeof("<empty>"),
+ name_len > 0 ? name : "<empty>");
+ return (MsgpackRpcRequestHandler){ 0 };
}
- return rv;
+ return method_handlers[hash];
}
-
-#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "api/private/dispatch_wrappers.generated.h"
-#endif
diff --git a/src/nvim/api/private/dispatch.h b/src/nvim/api/private/dispatch.h
index bad5a13934..4b7c394944 100644
--- a/src/nvim/api/private/dispatch.h
+++ b/src/nvim/api/private/dispatch.h
@@ -10,6 +10,7 @@ typedef Object (*ApiDispatchWrapper)(uint64_t channel_id,
/// The rpc_method_handlers table, used in msgpack_rpc_dispatch(), stores
/// functions of this type.
typedef struct {
+ const char *name;
ApiDispatchWrapper fn;
bool fast; // Function is safe to be executed immediately while running the
// uv loop (the loop is run very frequently due to breakcheck).
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index 8555d1bb71..228d114376 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -480,7 +480,7 @@ Object nvim_notify(String msg, Integer log_level, Dictionary opts, Error *err)
}
/// Calculates the number of display cells occupied by `text`.
-/// <Tab> counts as one cell.
+/// Control characters including <Tab> count as one cell.
///
/// @param text Some text
/// @param[out] err Error details, if any
diff --git a/src/nvim/channel.c b/src/nvim/channel.c
index 7a71be58c1..ecc3a24784 100644
--- a/src/nvim/channel.c
+++ b/src/nvim/channel.c
@@ -188,7 +188,7 @@ Channel *channel_alloc(ChannelStreamType type)
void channel_create_event(Channel *chan, const char *ext_source)
{
-#if MIN_LOG_LEVEL <= INFO_LOG_LEVEL
+#if MIN_LOG_LEVEL <= LOGLVL_INF
const char *source;
if (ext_source) {
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 02dc5ec954..6eb210fc79 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -10413,7 +10413,7 @@ char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, char *flags
// - The text up to where the match is.
// - The substituted text.
// - The text after the match.
- sublen = vim_regsub(&regmatch, (char_u *)sub, expr, (char_u *)tail, false, true, false);
+ sublen = vim_regsub(&regmatch, (char_u *)sub, expr, (char_u *)tail, 0, REGSUB_MAGIC);
ga_grow(&ga, (int)((end - tail) + sublen -
(regmatch.endp[0] - regmatch.startp[0])));
@@ -10421,8 +10421,9 @@ char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, char *flags
int i = (int)(regmatch.startp[0] - (char_u *)tail);
memmove((char_u *)ga.ga_data + ga.ga_len, tail, (size_t)i);
// add the substituted text
- (void)vim_regsub(&regmatch, (char_u *)sub, expr, (char_u *)ga.ga_data
- + ga.ga_len + i, true, true, false);
+ (void)vim_regsub(&regmatch, (char_u *)sub, expr,
+ (char_u *)ga.ga_data + ga.ga_len + i, sublen,
+ REGSUB_COPY | REGSUB_MAGIC);
ga.ga_len += i + sublen - 1;
tail = (char *)regmatch.endp[0];
if (*tail == NUL) {
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 93ff7bd752..7f75704de6 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -4071,7 +4071,8 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
// get length of substitution part
sublen = vim_regsub_multi(&regmatch,
sub_firstlnum - regmatch.startpos[0].lnum,
- (char_u *)sub, (char_u *)sub_firstline, false, p_magic, true);
+ (char_u *)sub, (char_u *)sub_firstline, 0,
+ REGSUB_BACKSLASH | (p_magic ? REGSUB_MAGIC : 0));
// If getting the substitute string caused an error, don't do
// the replacement.
// Don't keep flags set by a recursive call
@@ -4111,7 +4112,8 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
(void)vim_regsub_multi(&regmatch,
sub_firstlnum - regmatch.startpos[0].lnum,
- (char_u *)sub, (char_u *)new_end, true, p_magic, true);
+ (char_u *)sub, (char_u *)new_end, sublen,
+ REGSUB_COPY | REGSUB_BACKSLASH | (p_magic ? REGSUB_MAGIC : 0));
sub_nsubs++;
did_sub = true;
diff --git a/src/nvim/generators/gen_api_dispatch.lua b/src/nvim/generators/gen_api_dispatch.lua
index c6dd25154b..0f7052d351 100644
--- a/src/nvim/generators/gen_api_dispatch.lua
+++ b/src/nvim/generators/gen_api_dispatch.lua
@@ -16,6 +16,10 @@ local functions = {}
local nvimdir = arg[1]
package.path = nvimdir .. '/?.lua;' .. package.path
+_G.vim = loadfile(nvimdir..'/../../runtime/lua/vim/shared.lua')()
+
+local hashy = require'generators.hashy'
+
-- names of all headers relative to the source root (for inclusion in the
-- generated file)
local headers = {}
@@ -208,8 +212,8 @@ for i = 1, #functions do
output:write('Object handle_'..fn.name..'(uint64_t channel_id, Array args, Error *error)')
output:write('\n{')
- output:write('\n#if MIN_LOG_LEVEL <= DEBUG_LOG_LEVEL')
- output:write('\n logmsg(DEBUG_LOG_LEVEL, "RPC: ", NULL, -1, true, "ch %" PRIu64 ": invoke '
+ output:write('\n#if MIN_LOG_LEVEL <= LOGLVL_DBG')
+ output:write('\n logmsg(LOGLVL_DBG, "RPC: ", NULL, -1, true, "ch %" PRIu64 ": invoke '
..fn.name..'", channel_id);')
output:write('\n#endif')
output:write('\n Object ret = NIL;')
@@ -339,24 +343,27 @@ for i = 1, #functions do
end
end
--- Generate a function that initializes method names with handler functions
-output:write([[
-void msgpack_rpc_init_method_table(void)
-{
-]])
-
-for i = 1, #functions do
- local fn = functions[i]
+local remote_fns = {}
+for _,fn in ipairs(functions) do
if fn.remote then
- output:write(' msgpack_rpc_add_method_handler('..
- '(String) {.data = "'..fn.name..'", '..
- '.size = sizeof("'..fn.name..'") - 1}, '..
- '(MsgpackRpcRequestHandler) {.fn = handle_'.. (fn.impl_name or fn.name)..
- ', .fast = '..tostring(fn.fast)..'});\n')
+ remote_fns[fn.name] = fn
end
end
+remote_fns.redraw = {impl_name="ui_client_redraw", fast=true}
+
+local hashorder, hashfun = hashy.hashy_hash("msgpack_rpc_get_handler_for", vim.tbl_keys(remote_fns), function (idx)
+ return "method_handlers["..idx.."].name"
+end)
+
+output:write("static const MsgpackRpcRequestHandler method_handlers[] = {\n")
+for _, name in ipairs(hashorder) do
+ local fn = remote_fns[name]
+ output:write(' { .name = "'..name..'", .fn = handle_'.. (fn.impl_name or fn.name)..
+ ', .fast = '..tostring(fn.fast)..'},\n')
+end
+output:write("};\n\n")
+output:write(hashfun)
-output:write('\n}\n\n')
output:close()
local mpack_output = io.open(mpack_outputf, 'wb')
diff --git a/src/nvim/generators/gen_api_ui_events.lua b/src/nvim/generators/gen_api_ui_events.lua
index 5e70442dce..99dfac05e8 100755
--- a/src/nvim/generators/gen_api_ui_events.lua
+++ b/src/nvim/generators/gen_api_ui_events.lua
@@ -15,6 +15,9 @@ local client_output = io.open(arg[8], 'wb')
local c_grammar = require('generators.c_grammar')
local events = c_grammar.grammar:match(input:read('*all'))
+_G.vim = loadfile(nvimdir..'/../../runtime/lua/vim/shared.lua')()
+local hashy = require'generators.hashy'
+
local function write_signature(output, ev, prefix, notype)
output:write('('..prefix)
if prefix == "" and #ev.parameters == 0 then
@@ -213,24 +216,25 @@ for i = 1, #events do
end
end
--- Generate the map_init method for client handlers
-client_output:write([[
-void ui_client_methods_table_init(void)
-{
+local client_events = {}
+for _,ev in ipairs(events) do
+ if (not ev.noexport) and ((not ev.remote_only) or ev.client_impl) then
+ client_events[ev.name] = ev
+ end
+end
-]])
+local hashorder, hashfun = hashy.hashy_hash("ui_client_handler", vim.tbl_keys(client_events), function (idx)
+ return "event_handlers["..idx.."].name"
+end)
-for i = 1, #events do
- local fn = events[i]
- if (not fn.noexport) and ((not fn.remote_only) or fn.client_impl) then
- client_output:write(' add_ui_client_event_handler('..
- '(String) {.data = "'..fn.name..'", '..
- '.size = sizeof("'..fn.name..'") - 1}, '..
- '(UIClientHandler) ui_client_event_'..fn.name..');\n')
- end
+client_output:write("static const UIClientHandler event_handlers[] = {\n")
+
+for _, name in ipairs(hashorder) do
+ client_output:write(' { .name = "'..name..'", .fn = ui_client_event_'..name..'},\n')
end
-client_output:write('\n}\n\n')
+client_output:write('\n};\n\n')
+client_output:write(hashfun)
proto_output:close()
call_output:close()
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index fb3aa91c85..a42c8e979d 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -76,7 +76,8 @@
EXTERN struct nvim_stats_s {
int64_t fsync;
int64_t redraw;
-} g_stats INIT(= { 0, 0 });
+ int16_t log_skip; // How many logs were tried and skipped before log_init.
+} g_stats INIT(= { 0, 0, 0 });
// Values for "starting".
#define NO_SCREEN 2 // no screen updating yet
@@ -1032,7 +1033,7 @@ EXTERN FILE *time_fd INIT(= NULL); // where to write startup timing
// the warning.
EXTERN int vim_ignored;
-// Start a msgpack-rpc channel over stdin/stdout.
+// stdio is an RPC channel (--embed).
EXTERN bool embedded_mode INIT(= false);
// Do not start a UI nor read/write to stdio (unless embedding).
EXTERN bool headless_mode INIT(= false);
diff --git a/src/nvim/log.c b/src/nvim/log.c
index 815d53b570..c0a73a00fd 100644
--- a/src/nvim/log.c
+++ b/src/nvim/log.c
@@ -17,6 +17,8 @@
#include "auto/config.h"
#include "nvim/log.h"
+#include "nvim/main.h"
+#include "nvim/message.h"
#include "nvim/os/os.h"
#include "nvim/os/time.h"
#include "nvim/types.h"
@@ -26,6 +28,7 @@
/// Cached location of the expanded log file path decided by log_path_init().
static char log_file_path[MAXPATHL + 1] = { 0 };
+static bool did_log_init = false;
static uv_mutex_t mutex;
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -103,7 +106,9 @@ static bool log_path_init(void)
void log_init(void)
{
uv_mutex_init(&mutex);
+ // AFTER init_homedir ("~", XDG) and set_init_1 (env vars). 22b52dd462e5 #11501
log_path_init();
+ did_log_init = true;
}
void log_lock(void)
@@ -116,6 +121,14 @@ void log_unlock(void)
uv_mutex_unlock(&mutex);
}
+static void on_log_recursive_event(void **argv)
+{
+ char *fn_name = argv[0];
+ ptrdiff_t linenr = (ptrdiff_t)argv[1];
+ siemsg("E5430: %s:%d: recursive log!", fn_name, linenr);
+ xfree(fn_name);
+}
+
/// Logs a message to $NVIM_LOG_FILE.
///
/// @param log_level Log level (see log.h)
@@ -124,10 +137,29 @@ void log_unlock(void)
/// @param line_num Source line number, or -1
/// @param eol Append linefeed "\n"
/// @param fmt printf-style format string
+///
+/// @return true if log was emitted normally, false if failed or recursive
bool logmsg(int log_level, const char *context, const char *func_name, int line_num, bool eol,
const char *fmt, ...)
FUNC_ATTR_UNUSED FUNC_ATTR_PRINTF(6, 7)
{
+ static bool recursive = false;
+ static bool did_msg = false; // Showed recursion message?
+ if (!did_log_init) {
+ g_stats.log_skip++;
+ // set_init_1 may try logging before we are ready. 6f27f5ef91b3 #10183
+ return false;
+ }
+ if (recursive) {
+ if (!did_msg) {
+ did_msg = true;
+ char *arg1 = func_name ? xstrdup(func_name) : (context ? xstrdup(context) : NULL);
+ multiqueue_put(main_loop.events, on_log_recursive_event, 2, arg1, line_num);
+ }
+ g_stats.log_skip++;
+ return false;
+ }
+
if (log_level < MIN_LOG_LEVEL) {
return false;
}
@@ -139,6 +171,7 @@ bool logmsg(int log_level, const char *context, const char *func_name, int line_
#endif
log_lock();
+ recursive = true;
bool ret = false;
FILE *log_file = open_log_file();
@@ -156,6 +189,7 @@ bool logmsg(int log_level, const char *context, const char *func_name, int line_
fclose(log_file);
}
end:
+ recursive = false;
log_unlock();
return ret;
}
@@ -184,21 +218,17 @@ end:
/// @return FILE* decided by log_path_init() or stderr in case of error
FILE *open_log_file(void)
{
- static bool opening_log_file = false;
- // Disallow recursion. (This only matters for log_path_init; for logmsg and
- // friends we use a mutex: log_lock).
- if (opening_log_file) {
- do_log_to_file(stderr, ERROR_LOG_LEVEL, NULL, __func__, __LINE__, true,
- "Cannot LOG() recursively.");
- return stderr;
+ static bool recursive = false;
+ if (recursive) {
+ abort();
}
FILE *log_file = NULL;
- opening_log_file = true;
+ recursive = true;
if (log_path_init()) {
log_file = fopen(log_file_path, "a");
}
- opening_log_file = false;
+ recursive = false;
if (log_file != NULL) {
return log_file;
@@ -208,9 +238,8 @@ FILE *open_log_file(void)
// - LOG() is called before early_init()
// - Directory does not exist
// - File is not writable
- do_log_to_file(stderr, ERROR_LOG_LEVEL, NULL, __func__, __LINE__, true,
- "Logging to stderr, failed to open $" LOG_FILE_ENV ": %s",
- log_file_path);
+ do_log_to_file(stderr, LOGLVL_ERR, NULL, __func__, __LINE__, true,
+ "failed to open $" LOG_FILE_ENV ": %s", log_file_path);
return stderr;
}
@@ -237,8 +266,7 @@ void log_callstack_to_file(FILE *log_file, const char *const func_name, const in
// Now we have a command string like:
// addr2line -e /path/to/exe -f -p 0x123 0x456 ...
- do_log_to_file(log_file, DEBUG_LOG_LEVEL, NULL, func_name, line_num, true,
- "trace:");
+ do_log_to_file(log_file, LOGLVL_DBG, NULL, func_name, line_num, true, "trace:");
FILE *fp = popen(cmdbuf, "r");
char linebuf[IOSIZE];
while (fgets(linebuf, sizeof(linebuf) - 1, fp) != NULL) {
@@ -285,12 +313,12 @@ static bool v_do_log_to_file(FILE *log_file, int log_level, const char *context,
FUNC_ATTR_PRINTF(7, 0)
{
static const char *log_levels[] = {
- [DEBUG_LOG_LEVEL] = "DEBUG",
- [INFO_LOG_LEVEL] = "INFO ",
- [WARN_LOG_LEVEL] = "WARN ",
- [ERROR_LOG_LEVEL] = "ERROR",
+ [LOGLVL_DBG] = "DBG",
+ [LOGLVL_INF] = "INF",
+ [LOGLVL_WRN] = "WRN",
+ [LOGLVL_ERR] = "ERR",
};
- assert(log_level >= DEBUG_LOG_LEVEL && log_level <= ERROR_LOG_LEVEL);
+ assert(log_level >= LOGLVL_DBG && log_level <= LOGLVL_ERR);
// Format the timestamp.
struct tm local_time;
diff --git a/src/nvim/log.h b/src/nvim/log.h
index 724d073d02..cbee0e0f81 100644
--- a/src/nvim/log.h
+++ b/src/nvim/log.h
@@ -16,11 +16,11 @@
# define NVIM_PROBE(name, n, ...)
#endif
-#define TRACE_LOG_LEVEL 0
-#define DEBUG_LOG_LEVEL 1
-#define INFO_LOG_LEVEL 2
-#define WARN_LOG_LEVEL 3
-#define ERROR_LOG_LEVEL 4
+#define LOGLVL_TRC 0
+#define LOGLVL_DBG 1
+#define LOGLVL_INF 2
+#define LOGLVL_WRN 3
+#define LOGLVL_ERR 4
#define DLOG(...)
#define DLOGN(...)
@@ -32,46 +32,37 @@
#define ELOGN(...)
#ifndef MIN_LOG_LEVEL
-# define MIN_LOG_LEVEL INFO_LOG_LEVEL
+# define MIN_LOG_LEVEL LOGLVL_INF
#endif
-#define LOG(level, ...) logmsg((level), NULL, __func__, __LINE__, true, \
- __VA_ARGS__)
+#define LOG(level, ...) logmsg((level), NULL, __func__, __LINE__, true, __VA_ARGS__)
-#if MIN_LOG_LEVEL <= DEBUG_LOG_LEVEL
+#if MIN_LOG_LEVEL <= LOGLVL_DBG
# undef DLOG
# undef DLOGN
-# define DLOG(...) logmsg(DEBUG_LOG_LEVEL, NULL, __func__, __LINE__, true, \
- __VA_ARGS__)
-# define DLOGN(...) logmsg(DEBUG_LOG_LEVEL, NULL, __func__, __LINE__, false, \
- __VA_ARGS__)
+# define DLOG(...) logmsg(LOGLVL_DBG, NULL, __func__, __LINE__, true, __VA_ARGS__)
+# define DLOGN(...) logmsg(LOGLVL_DBG, NULL, __func__, __LINE__, false, __VA_ARGS__)
#endif
-#if MIN_LOG_LEVEL <= INFO_LOG_LEVEL
+#if MIN_LOG_LEVEL <= LOGLVL_INF
# undef ILOG
# undef ILOGN
-# define ILOG(...) logmsg(INFO_LOG_LEVEL, NULL, __func__, __LINE__, true, \
- __VA_ARGS__)
-# define ILOGN(...) logmsg(INFO_LOG_LEVEL, NULL, __func__, __LINE__, false, \
- __VA_ARGS__)
+# define ILOG(...) logmsg(LOGLVL_INF, NULL, __func__, __LINE__, true, __VA_ARGS__)
+# define ILOGN(...) logmsg(LOGLVL_INF, NULL, __func__, __LINE__, false, __VA_ARGS__)
#endif
-#if MIN_LOG_LEVEL <= WARN_LOG_LEVEL
+#if MIN_LOG_LEVEL <= LOGLVL_WRN
# undef WLOG
# undef WLOGN
-# define WLOG(...) logmsg(WARN_LOG_LEVEL, NULL, __func__, __LINE__, true, \
- __VA_ARGS__)
-# define WLOGN(...) logmsg(WARN_LOG_LEVEL, NULL, __func__, __LINE__, false, \
- __VA_ARGS__)
+# define WLOG(...) logmsg(LOGLVL_WRN, NULL, __func__, __LINE__, true, __VA_ARGS__)
+# define WLOGN(...) logmsg(LOGLVL_WRN, NULL, __func__, __LINE__, false, __VA_ARGS__)
#endif
-#if MIN_LOG_LEVEL <= ERROR_LOG_LEVEL
+#if MIN_LOG_LEVEL <= LOGLVL_ERR
# undef ELOG
# undef ELOGN
-# define ELOG(...) logmsg(ERROR_LOG_LEVEL, NULL, __func__, __LINE__, true, \
- __VA_ARGS__)
-# define ELOGN(...) logmsg(ERROR_LOG_LEVEL, NULL, __func__, __LINE__, false, \
- __VA_ARGS__)
+# define ELOG(...) logmsg(LOGLVL_ERR, NULL, __func__, __LINE__, true, __VA_ARGS__)
+# define ELOGN(...) logmsg(LOGLVL_ERR, NULL, __func__, __LINE__, false, __VA_ARGS__)
#endif
#ifdef HAVE_EXECINFO_BACKTRACE
diff --git a/src/nvim/main.c b/src/nvim/main.c
index 936b42be23..fabfc57037 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -121,7 +121,6 @@ void event_init(void)
resize_events = multiqueue_new_child(main_loop.events);
// early msgpack-rpc initialization
- msgpack_rpc_init_method_table();
msgpack_rpc_helpers_init();
input_init();
signal_init();
diff --git a/src/nvim/map.c b/src/nvim/map.c
index 05ad113008..d27e40b4ee 100644
--- a/src/nvim/map.c
+++ b/src/nvim/map.c
@@ -14,7 +14,6 @@
#include <stdlib.h>
#include <string.h>
-#include "nvim/api/private/dispatch.h"
#include "nvim/lib/khash.h"
#include "nvim/map.h"
#include "nvim/map_defs.h"
@@ -171,13 +170,10 @@ MAP_IMPL(uint64_t, ssize_t, SSIZE_INITIALIZER)
MAP_IMPL(uint64_t, uint64_t, DEFAULT_INITIALIZER)
MAP_IMPL(uint32_t, uint32_t, DEFAULT_INITIALIZER)
MAP_IMPL(handle_T, ptr_t, DEFAULT_INITIALIZER)
-#define MSGPACK_HANDLER_INITIALIZER { .fn = NULL, .fast = false }
-MAP_IMPL(String, MsgpackRpcRequestHandler, MSGPACK_HANDLER_INITIALIZER)
MAP_IMPL(HlEntry, int, DEFAULT_INITIALIZER)
MAP_IMPL(String, handle_T, 0)
MAP_IMPL(String, int, DEFAULT_INITIALIZER)
MAP_IMPL(int, String, DEFAULT_INITIALIZER)
-MAP_IMPL(String, UIClientHandler, NULL)
MAP_IMPL(ColorKey, ColorItem, COLOR_ITEM_INITIALIZER)
diff --git a/src/nvim/map.h b/src/nvim/map.h
index 693ef50127..4f4aaa3552 100644
--- a/src/nvim/map.h
+++ b/src/nvim/map.h
@@ -4,7 +4,6 @@
#include <stdbool.h>
#include "nvim/api/private/defs.h"
-#include "nvim/api/private/dispatch.h"
#include "nvim/extmark_defs.h"
#include "nvim/highlight_defs.h"
#include "nvim/map_defs.h"
@@ -44,12 +43,10 @@ MAP_DECLS(uint64_t, uint64_t)
MAP_DECLS(uint32_t, uint32_t)
MAP_DECLS(handle_T, ptr_t)
-MAP_DECLS(String, MsgpackRpcRequestHandler)
MAP_DECLS(HlEntry, int)
MAP_DECLS(String, handle_T)
MAP_DECLS(String, int)
MAP_DECLS(int, String)
-MAP_DECLS(String, UIClientHandler)
MAP_DECLS(ColorKey, ColorItem)
diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c
index 79ecd9f827..287310cc34 100644
--- a/src/nvim/msgpack_rpc/channel.c
+++ b/src/nvim/msgpack_rpc/channel.c
@@ -31,7 +31,7 @@
#include "nvim/ui.h"
#include "nvim/vim.h"
-#if MIN_LOG_LEVEL > DEBUG_LOG_LEVEL
+#if MIN_LOG_LEVEL > LOGLVL_DBG
# define log_client_msg(...)
# define log_server_msg(...)
#endif
@@ -62,7 +62,7 @@ void rpc_start(Channel *channel)
if (channel->streamtype != kChannelStreamInternal) {
Stream *out = channel_outstream(channel);
-#if MIN_LOG_LEVEL <= DEBUG_LOG_LEVEL
+#if MIN_LOG_LEVEL <= LOGLVL_DBG
Stream *in = channel_instream(channel);
DLOG("rpc ch %" PRIu64 " in-stream=%p out-stream=%p", channel->id,
(void *)in, (void *)out);
@@ -209,7 +209,7 @@ static void receive_msgpack(Stream *stream, RBuffer *rbuf, size_t c, void *data,
char buf[256];
snprintf(buf, sizeof(buf), "ch %" PRIu64 " was closed by the client",
channel->id);
- call_set_error(channel, buf, INFO_LOG_LEVEL);
+ call_set_error(channel, buf, LOGLVL_INF);
goto end;
}
@@ -249,7 +249,7 @@ static void parse_msgpack(Channel *channel)
"ch %" PRIu64 " returned a response with an unknown request "
"id. Ensure the client is properly synchronized",
channel->id);
- call_set_error(channel, buf, ERROR_LOG_LEVEL);
+ call_set_error(channel, buf, LOGLVL_ERR);
}
msgpack_unpacked_destroy(&unpacked);
} else {
@@ -299,7 +299,7 @@ static void handle_request(Channel *channel, msgpack_object *request)
snprintf(buf, sizeof(buf),
"ch %" PRIu64 " sent an invalid message, closed.",
channel->id);
- call_set_error(channel, buf, ERROR_LOG_LEVEL);
+ call_set_error(channel, buf, LOGLVL_ERR);
}
api_clear_error(&error);
return;
@@ -418,7 +418,7 @@ static bool channel_write(Channel *channel, WBuffer *buffer)
"ch %" PRIu64 ": stream write failed. "
"RPC canceled; closing channel",
channel->id);
- call_set_error(channel, buf, ERROR_LOG_LEVEL);
+ call_set_error(channel, buf, LOGLVL_ERR);
}
return success;
@@ -693,7 +693,7 @@ const char *rpc_client_name(Channel *chan)
return NULL;
}
-#if MIN_LOG_LEVEL <= DEBUG_LOG_LEVEL
+#if MIN_LOG_LEVEL <= LOGLVL_DBG
# define REQ "[request] "
# define RES "[response] "
# define NOT "[notify] "
diff --git a/src/nvim/msgpack_rpc/channel_defs.h b/src/nvim/msgpack_rpc/channel_defs.h
index 6647779db9..65f1e6b6d6 100644
--- a/src/nvim/msgpack_rpc/channel_defs.h
+++ b/src/nvim/msgpack_rpc/channel_defs.h
@@ -6,6 +6,7 @@
#include <uv.h>
#include "nvim/api/private/defs.h"
+#include "nvim/api/private/dispatch.h"
#include "nvim/event/process.h"
#include "nvim/event/socket.h"
#include "nvim/vim.h"
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index 6b8c75e430..44c9928f7b 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -1653,21 +1653,22 @@ static void clear_submatch_list(staticList10_T *sl)
/// vim_regsub() - perform substitutions after a vim_regexec() or
/// vim_regexec_multi() match.
///
-/// If "copy" is true really copy into "dest".
-/// If "copy" is false nothing is copied, this is just to find out the length
-/// of the result.
+/// If "flags" has REGSUB_COPY really copy into "dest[destlen]".
+/// Oterwise nothing is copied, only compue the length of the result.
///
-/// If "backslash" is true, a backslash will be removed later, need to double
-/// them to keep them, and insert a backslash before a CR to avoid it being
-/// replaced with a line break later.
+/// If "flags" has REGSUB_MAGIC then behave like 'magic' is set.
+///
+/// If "flags" has REGSUB_BACKSLASH a backslash will be removed later, need to
+/// double them to keep them, and insert a backslash before a CR to avoid it
+/// being replaced with a line break later.
///
/// Note: The matched text must not change between the call of
/// vim_regexec()/vim_regexec_multi() and vim_regsub()! It would make the back
/// references invalid!
///
/// Returns the size of the replacement, including terminating NUL.
-int vim_regsub(regmatch_T *rmp, char_u *source, typval_T *expr, char_u *dest, int copy, int magic,
- int backslash)
+int vim_regsub(regmatch_T *rmp, char_u *source, typval_T *expr, char_u *dest, int destlen,
+ int flags)
{
regexec_T rex_save;
bool rex_in_use_save = rex_in_use;
@@ -1683,7 +1684,7 @@ int vim_regsub(regmatch_T *rmp, char_u *source, typval_T *expr, char_u *dest, in
rex.reg_maxline = 0;
rex.reg_buf = curbuf;
rex.reg_line_lbr = true;
- int result = vim_regsub_both(source, expr, dest, copy, magic, backslash);
+ int result = vim_regsub_both(source, expr, dest, destlen, flags);
rex_in_use = rex_in_use_save;
if (rex_in_use) {
@@ -1693,8 +1694,8 @@ int vim_regsub(regmatch_T *rmp, char_u *source, typval_T *expr, char_u *dest, in
return result;
}
-int vim_regsub_multi(regmmatch_T *rmp, linenr_T lnum, char_u *source, char_u *dest, int copy,
- int magic, int backslash)
+int vim_regsub_multi(regmmatch_T *rmp, linenr_T lnum, char_u *source, char_u *dest, int destlen,
+ int flags)
{
regexec_T rex_save;
bool rex_in_use_save = rex_in_use;
@@ -1711,7 +1712,7 @@ int vim_regsub_multi(regmmatch_T *rmp, linenr_T lnum, char_u *source, char_u *de
rex.reg_firstlnum = lnum;
rex.reg_maxline = curbuf->b_ml.ml_line_count - lnum;
rex.reg_line_lbr = false;
- int result = vim_regsub_both(source, NULL, dest, copy, magic, backslash);
+ int result = vim_regsub_both(source, NULL, dest, destlen, flags);
rex_in_use = rex_in_use_save;
if (rex_in_use) {
@@ -1721,8 +1722,7 @@ int vim_regsub_multi(regmmatch_T *rmp, linenr_T lnum, char_u *source, char_u *de
return result;
}
-static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int copy, int magic,
- int backslash)
+static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int destlen, int flags)
{
char_u *src;
char_u *dst;
@@ -1735,6 +1735,7 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int cop
linenr_T clnum = 0; // init for GCC
int len = 0; // init for GCC
static char_u *eval_result = NULL;
+ bool copy = flags & REGSUB_COPY;
// We need to keep track of how many backslashes we escape, so that the byte
// counts for `extmark_splice` are correct.
@@ -1755,8 +1756,8 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int cop
if (expr != NULL || (source[0] == '\\' && source[1] == '=')) {
// To make sure that the length doesn't change between checking the
// length and copying the string, and to speed up things, the
- // resulting string is saved from the call with "copy" == false to the
- // call with "copy" == true.
+ // resulting string is saved from the call with "flags & REGSUB_COPY"
+ // == 0 to the call with "flags & REGSUB_COPY" != 0.
if (copy) {
if (eval_result != NULL) {
STRCPY(dest, eval_result);
@@ -1845,7 +1846,7 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int cop
had_backslash = true;
}
}
- if (had_backslash && backslash) {
+ if (had_backslash && (flags & REGSUB_BACKSLASH)) {
// Backslashes will be consumed, need to double them.
s = vim_strsave_escaped(eval_result, (char_u *)"\\");
xfree(eval_result);
@@ -1862,11 +1863,11 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int cop
}
} else {
while ((c = *src++) != NUL) {
- if (c == '&' && magic) {
+ if (c == '&' && (flags & REGSUB_MAGIC)) {
no = 0;
} else if (c == '\\' && *src != NUL) {
- if (*src == '&' && !magic) {
- ++src;
+ if (*src == '&' && !(flags & REGSUB_MAGIC)) {
+ src++;
no = 0;
} else if ('0' <= *src && *src <= '9') {
no = *src++ - '0';
@@ -1895,6 +1896,10 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int cop
if (c == K_SPECIAL && src[0] != NUL && src[1] != NUL) {
// Copy a special key as-is.
if (copy) {
+ if (dst + 3 > dest + destlen) {
+ iemsg("vim_regsub_both(): not enough space");
+ return 0;
+ }
*dst++ = c;
*dst++ = *src++;
*dst++ = *src++;
@@ -1922,9 +1927,13 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int cop
// If "backslash" is true the backslash will be removed
// later. Used to insert a literal CR.
default:
- if (backslash) {
+ if (flags & REGSUB_BACKSLASH) {
num_escaped += 1;
if (copy) {
+ if (dst + 1 > dest + destlen) {
+ iemsg("vim_regsub_both(): not enough space");
+ return 0;
+ }
*dst = '\\';
}
dst++;
@@ -1945,17 +1954,26 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int cop
}
int totlen = utfc_ptr2len((char *)src - 1);
+ int charlen = utf_char2len(cc);
if (copy) {
+ if (dst + charlen > dest + destlen) {
+ iemsg("vim_regsub_both(): not enough space");
+ return 0;
+ }
utf_char2bytes(cc, (char *)dst);
}
- dst += utf_char2len(cc) - 1;
+ dst += charlen - 1;
int clen = utf_ptr2len((char *)src - 1);
// If the character length is shorter than "totlen", there
// are composing characters; copy them as-is.
if (clen < totlen) {
if (copy) {
+ if (dst + totlen - clen > dest + destlen) {
+ iemsg("vim_regsub_both(): not enough space");
+ return 0;
+ }
memmove(dst + 1, src - 1 + clen, (size_t)(totlen - clen));
}
dst += totlen - clen;
@@ -1992,6 +2010,10 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int cop
break;
}
if (copy) {
+ if (dst + 1 > dest + destlen) {
+ iemsg("vim_regsub_both(): not enough space");
+ return 0;
+ }
*dst = CAR;
}
dst++;
@@ -2010,14 +2032,16 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int cop
}
goto exit;
} else {
- if (backslash && (*s == CAR || *s == '\\')) {
- /*
- * Insert a backslash in front of a CR, otherwise
- * it will be replaced by a line break.
- * Number of backslashes will be halved later,
- * double them here.
- */
+ if ((flags & REGSUB_BACKSLASH) && (*s == CAR || *s == '\\')) {
+ // Insert a backslash in front of a CR, otherwise
+ // it will be replaced by a line break.
+ // Number of backslashes will be halved later,
+ // double them here.
if (copy) {
+ if (dst + 2 > dest + destlen) {
+ iemsg("vim_regsub_both(): not enough space");
+ return 0;
+ }
dst[0] = '\\';
dst[1] = *s;
}
@@ -2037,6 +2061,7 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int cop
{
int l;
+ int charlen;
// Copy composing characters separately, one
// at a time.
@@ -2044,10 +2069,15 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int cop
s += l;
len -= l;
+ charlen = utf_char2len(cc);
if (copy) {
+ if (dst + charlen > dest + destlen) {
+ iemsg("vim_regsub_both(): not enough space");
+ return 0;
+ }
utf_char2bytes(cc, (char *)dst);
}
- dst += utf_char2len(cc) - 1;
+ dst += charlen - 1;
}
dst++;
}
@@ -2386,8 +2416,8 @@ static void report_re_switch(char_u *pat)
}
}
-/// Matches a regexp against a string.
-/// "rmp->regprog" is a compiled regexp as returned by vim_regcomp().
+/// Match a regexp against a string.
+/// "rmp->regprog" must be a compiled regexp as returned by vim_regcomp().
/// Note: "rmp->regprog" may be freed and changed.
/// Uses curbuf for line count and 'iskeyword'.
/// When "nl" is true consider a "\n" in "line" to be a line break.
diff --git a/src/nvim/regexp_defs.h b/src/nvim/regexp_defs.h
index decc832051..09f244c2f6 100644
--- a/src/nvim/regexp_defs.h
+++ b/src/nvim/regexp_defs.h
@@ -168,4 +168,9 @@ struct regengine {
// char_u *expr;
};
+// Flags used by vim_regsub() and vim_regsub_both()
+#define REGSUB_COPY 1
+#define REGSUB_MAGIC 2
+#define REGSUB_BACKSLASH 4
+
#endif // NVIM_REGEXP_DEFS_H
diff --git a/src/nvim/state.c b/src/nvim/state.c
index f6d9b535fc..f075dd772a 100644
--- a/src/nvim/state.c
+++ b/src/nvim/state.c
@@ -81,8 +81,8 @@ getkey:
may_sync_undo();
}
-#if MIN_LOG_LEVEL <= DEBUG_LOG_LEVEL
- log_key(DEBUG_LOG_LEVEL, key);
+#if MIN_LOG_LEVEL <= LOGLVL_DBG
+ log_key(LOGLVL_DBG, key);
#endif
int execute_result = s->execute(s, key);
diff --git a/src/nvim/testdir/test_filetype_lua.vim b/src/nvim/testdir/test_filetype_lua.vim
index f73e4ca33f..d9c0dcba9c 100644
--- a/src/nvim/testdir/test_filetype_lua.vim
+++ b/src/nvim/testdir/test_filetype_lua.vim
@@ -1,2 +1,3 @@
let g:do_filetype_lua = 1
+let g:did_load_filetypes = 0
source test_filetype.vim
diff --git a/src/nvim/ui.c b/src/nvim/ui.c
index b40033296e..d66e57b13b 100644
--- a/src/nvim/ui.c
+++ b/src/nvim/ui.c
@@ -64,7 +64,7 @@ static handle_T cursor_grid_handle = DEFAULT_GRID_HANDLE;
static bool has_mouse = false;
static int pending_has_mouse = -1;
-#if MIN_LOG_LEVEL > DEBUG_LOG_LEVEL
+#if MIN_LOG_LEVEL > LOGLVL_DBG
# define UI_LOG(funname)
#else
static size_t uilog_seen = 0;
@@ -82,10 +82,10 @@ static char uilog_last_event[1024] = { 0 };
uilog_seen++; \
} else { \
if (uilog_seen > 0) { \
- logmsg(DEBUG_LOG_LEVEL, "UI: ", NULL, -1, true, \
+ logmsg(LOGLVL_DBG, "UI: ", NULL, -1, true, \
"%s (+%zu times...)", uilog_last_event, uilog_seen); \
} \
- logmsg(DEBUG_LOG_LEVEL, "UI: ", NULL, -1, true, STR(funname)); \
+ logmsg(LOGLVL_DBG, "UI: ", NULL, -1, true, STR(funname)); \
uilog_seen = 0; \
xstrlcpy(uilog_last_event, STR(funname), sizeof(uilog_last_event)); \
} \
diff --git a/src/nvim/ui_client.c b/src/nvim/ui_client.c
index 4d1b9b1c52..be01538f67 100644
--- a/src/nvim/ui_client.c
+++ b/src/nvim/ui_client.c
@@ -16,18 +16,17 @@
#include "nvim/ui_client.h"
#include "nvim/vim.h"
-static Map(String, UIClientHandler) ui_client_handlers = MAP_INIT;
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "ui_client.c.generated.h"
+
+# include "ui_events_client.generated.h"
+#endif
// Temporary buffer for converting a single grid_line event
static size_t buf_size = 0;
static schar_T *buf_char = NULL;
static sattr_T *buf_attr = NULL;
-static void add_ui_client_event_handler(String method, UIClientHandler handler)
-{
- map_put(String, UIClientHandler)(&ui_client_handlers, method, handler);
-}
-
void ui_client_init(uint64_t chan)
{
Array args = ARRAY_DICT_INIT;
@@ -44,9 +43,6 @@ void ui_client_init(uint64_t chan)
ADD(args, DICTIONARY_OBJ(opts));
rpc_send_event(chan, "nvim_ui_attach", args);
- msgpack_rpc_add_redraw(); // GAME!
- // TODO(bfredl): use a keyset instead
- ui_client_methods_table_init();
ui_client_channel_id = chan;
}
@@ -61,22 +57,23 @@ void ui_client_init(uint64_t chan)
/// @param channel_id: The id of the rpc channel
/// @param uidata: The dense array containing the ui_events sent by the server
/// @param[out] err Error details, if any
-Object ui_client_handle_redraw(uint64_t channel_id, Array args, Error *error)
+Object handle_ui_client_redraw(uint64_t channel_id, Array args, Error *error)
{
for (size_t i = 0; i < args.size; i++) {
Array call = args.items[i].data.array;
String name = call.items[0].data.string;
- UIClientHandler handler = map_get(String, UIClientHandler)(&ui_client_handlers, name);
- if (!handler) {
+ int hash = ui_client_handler_hash(name.data, name.size);
+ if (hash < 0) {
ELOG("No ui client handler for %s", name.size ? name.data : "<empty>");
continue;
}
+ UIClientHandler handler = event_handlers[hash];
// fprintf(stderr, "%s: %zu\n", name.data, call.size-1);
DLOG("Invoke ui client handler for %s", name.data);
for (size_t j = 1; j < call.size; j++) {
- handler(call.items[j].data.array);
+ handler.fn(call.items[j].data.array);
}
}
@@ -108,10 +105,6 @@ static HlAttrs ui_client_dict2hlattrs(Dictionary d, bool rgb)
return dict2hlattrs(&dict, true, NULL, &err);
}
-#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "ui_events_client.generated.h"
-#endif
-
void ui_client_event_grid_resize(Array args)
{
if (args.size < 3
diff --git a/src/nvim/ui_client.h b/src/nvim/ui_client.h
index d31341ae60..41d9fa6227 100644
--- a/src/nvim/ui_client.h
+++ b/src/nvim/ui_client.h
@@ -3,7 +3,10 @@
#include "nvim/api/private/defs.h"
-typedef void (*UIClientHandler)(Array args);
+typedef struct {
+ const char *name;
+ void (*fn)(Array args);
+} UIClientHandler;
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ui_client.h.generated.h"
diff --git a/test/functional/core/startup_spec.lua b/test/functional/core/startup_spec.lua
index f87fd8e951..18d48efadc 100644
--- a/test/functional/core/startup_spec.lua
+++ b/test/functional/core/startup_spec.lua
@@ -255,6 +255,7 @@ describe('startup', function()
it('does not crash when expanding cdpath during early_init', function()
clear{env={CDPATH='~doesnotexist'}}
+ assert_alive()
eq(',~doesnotexist', eval('&cdpath'))
end)
diff --git a/third-party/CMakeLists.txt b/third-party/CMakeLists.txt
index 06c3d47104..9c53649eea 100644
--- a/third-party/CMakeLists.txt
+++ b/third-party/CMakeLists.txt
@@ -165,8 +165,8 @@ set(LIBVTERM_URL https://www.leonerd.org.uk/code/libvterm/libvterm-0.1.4.tar.gz)
set(LIBVTERM_SHA256 bc70349e95559c667672fc8c55b9527d9db9ada0fb80a3beda533418d782d3dd)
set(LUV_VERSION 1.43.0-0)
-set(LUV_URL https://github.com/luvit/luv/archive/c51e7052ec4f0a25058f70c1b4ee99dd36180e59.tar.gz)
-set(LUV_SHA256 cabb7e650f35992686eb95ae167c71614e281cd2979fc804e4e70f8051555728)
+set(LUV_URL https://github.com/luvit/luv/archive/02d703b42be44d31483582a6538e885fa9b37134.tar.gz)
+set(LUV_SHA256 50831acb9b4a48dd83d942a6da06f405e46ca7125547605bac3e6a9c8efc190a)
set(LUA_COMPAT53_URL https://github.com/keplerproject/lua-compat-5.3/archive/v0.9.tar.gz)
set(LUA_COMPAT53_SHA256 ad05540d2d96a48725bb79a1def35cf6652a4e2ec26376e2617c8ce2baa6f416)