diff options
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(®match, (char_u *)sub, expr, (char_u *)tail, false, true, false); + sublen = vim_regsub(®match, (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(®match, (char_u *)sub, expr, (char_u *)ga.ga_data - + ga.ga_len + i, true, true, false); + (void)vim_regsub(®match, (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(®match, 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(®match, 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) |