From 1f847edc63cff8b1990023389948d2470cd4e40e Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 27 Jul 2022 14:11:51 +0100 Subject: refactor(cmd): format do_one_cmd() - Comment style - Minimise scope of locals --- src/nvim/ex_docmd.c | 133 +++++++++++++++++++--------------------------------- 1 file changed, 47 insertions(+), 86 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index a7d91a47d7..2343978ed8 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -1724,16 +1724,11 @@ end: static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter fgetline, void *cookie) { - char *p; - linenr_T lnum; char *errormsg = NULL; // error message - char *after_modifier = NULL; - exarg_T ea; - cmdmod_T save_cmdmod; const int save_reg_executing = reg_executing; const bool save_pending_end_reg_executing = pending_end_reg_executing; - char *cmd; + exarg_T ea; memset(&ea, 0, sizeof(ea)); ea.line1 = 1; ea.line2 = 1; @@ -1749,11 +1744,9 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter --quitmore; } - /* - * Reset browse, confirm, etc.. They are restored when returning, for - * recursive calls. - */ - save_cmdmod = cmdmod; + // Reset browse, confirm, etc.. They are restored when returning, for + // recursive calls. + cmdmod_T save_cmdmod = cmdmod; // "#!anything" is handled like a comment. if ((*cmdlinep)[0] == '#' && (*cmdlinep)[1] == '!') { @@ -1775,7 +1768,7 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter } apply_cmdmod(&cmdmod); - after_modifier = ea.cmd; + char *after_modifier = ea.cmd; ea.skip = (did_emsg || got_int @@ -1786,12 +1779,12 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter // 3. Skip over the range to find the command. Let "p" point to after it. // // We need the command to know what kind of range it uses. - cmd = ea.cmd; + char *cmd = ea.cmd; ea.cmd = skip_range(ea.cmd, NULL); if (*ea.cmd == '*') { ea.cmd = skipwhite(ea.cmd + 1); } - p = find_ex_command(&ea, NULL); + char *p = find_ex_command(&ea, NULL); // Count this line for profiling if skip is TRUE. if (do_profiling == PROF_YES @@ -1855,19 +1848,13 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter goto doend; } - /* - * 5. Parse the command. - */ + // 5. Parse the command. - /* - * Skip ':' and any white space - */ + // Skip ':' and any white space ea.cmd = skip_colon_white(ea.cmd, true); - /* - * If we got a line, but no command, then go to the line. - * If we find a '|' or '\n' we set ea.nextcmd. - */ + // If we got a line, but no command, then go to the line. + // If we find a '|' or '\n' we set ea.nextcmd. if (*ea.cmd == NUL || *ea.cmd == '"' || (ea.nextcmd = (char *)check_nextcmd((char_u *)ea.cmd)) != NULL) { // strange vi behaviour: @@ -2012,10 +1999,8 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter goto doend; } - /* - * Don't complain about the range if it is not used - * (could happen if line_count is accidentally set to 0). - */ + // Don't complain about the range if it is not used + // (could happen if line_count is accidentally set to 0). if (!ea.skip && !ni && (ea.argt & EX_RANGE)) { // If the range is backwards, ask for confirmation and, if given, swap // ea.line1 & ea.line2 so it's forwards again. @@ -2030,7 +2015,7 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter goto doend; } } - lnum = ea.line1; + linenr_T lnum = ea.line1; ea.line1 = ea.line2; ea.line2 = lnum; } @@ -2054,19 +2039,15 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter (void)hasFolding(ea.line2, NULL, &ea.line2); } - /* - * For the ":make" and ":grep" commands we insert the 'makeprg'/'grepprg' - * option here, so things like % get expanded. - */ + // For the ":make" and ":grep" commands we insert the 'makeprg'/'grepprg' + // option here, so things like % get expanded. p = replace_makeprg(&ea, p, cmdlinep); if (p == NULL) { goto doend; } - /* - * Skip to start of argument. - * Don't do this for the ":!" command, because ":!! -l" needs the space. - */ + // Skip to start of argument. + // Don't do this for the ":!" command, because ":!! -l" needs the space. if (ea.cmdidx == CMD_bang) { ea.arg = p; } else { @@ -2078,10 +2059,8 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter goto doend; } - /* - * Check for "++opt=val" argument. - * Must be first, allow ":w ++enc=utf8 !cmd" - */ + // Check for "++opt=val" argument. + // Must be first, allow ":w ++enc=utf8 !cmd" if (ea.argt & EX_ARGOPT) { while (ea.arg[0] == '+' && ea.arg[1] == '+') { if (getargopt(&ea) == FAIL && !ni) { @@ -2124,18 +2103,14 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter ea.arg = skipwhite(ea.arg); } - /* - * Check for "+command" argument, before checking for next command. - * Don't do this for ":read !cmd" and ":write !cmd". - */ + // Check for "+command" argument, before checking for next command. + // Don't do this for ":read !cmd" and ":write !cmd". if ((ea.argt & EX_CMDARG) && !ea.usefilter) { ea.do_ecmd_cmd = getargcmd(&ea.arg); } - /* - * Check for '|' to separate commands and '"' to start comments. - * Don't do this for ":read !cmd" and ":write !cmd". - */ + // Check for '|' to separate commands and '"' to start comments. + // Don't do this for ":read !cmd" and ":write !cmd". if ((ea.argt & EX_TRLBAR) && !ea.usefilter) { separate_nextcmd(&ea); } else if (ea.cmdidx == CMD_bang @@ -2146,18 +2121,18 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter // Check for to end a shell command. // Also do this for ":read !cmd", ":write !cmd" and ":global". // Any others? - for (p = ea.arg; *p; p++) { + for (char *s = ea.arg; *s; s++) { // Remove one backslash before a newline, so that it's possible to // pass a newline to the shell and also a newline that is preceded // with a backslash. This makes it impossible to end a shell // command in a backslash, but that doesn't appear useful. // Halving the number of backslashes is incompatible with previous // versions. - if (*p == '\\' && p[1] == '\n') { - STRMOVE(p, p + 1); - } else if (*p == '\n') { - ea.nextcmd = p + 1; - *p = NUL; + if (*s == '\\' && s[1] == '\n') { + STRMOVE(s, s + 1); + } else if (*s == '\n') { + ea.nextcmd = s + 1; + *s = NUL; break; } } @@ -2173,9 +2148,7 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter goto doend; } - /* - * Check for flags: 'l', 'p' and '#'. - */ + // Check for flags: 'l', 'p' and '#'. if (ea.argt & EX_FLAGS) { get_flags(&ea); } @@ -2191,12 +2164,10 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter goto doend; } - /* - * Skip the command when it's not going to be executed. - * The commands like :if, :endif, etc. always need to be executed. - * Also make an exception for commands that handle a trailing command - * themselves. - */ + // Skip the command when it's not going to be executed. + // The commands like :if, :endif, etc. always need to be executed. + // Also make an exception for commands that handle a trailing command + // themselves. if (ea.skip) { switch (ea.cmdidx) { // commands that need evaluation @@ -2295,17 +2266,13 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter } } - /* - * Accept buffer name. Cannot be used at the same time with a buffer - * number. Don't do this for a user command. - */ + // Accept buffer name. Cannot be used at the same time with a buffer + // number. Don't do this for a user command. if ((ea.argt & EX_BUFNAME) && *ea.arg != NUL && ea.addr_count == 0 && !IS_USER_CMDIDX(ea.cmdidx)) { - /* - * :bdelete, :bwipeout and :bunload take several arguments, separated - * by spaces: find next space (skipping over escaped characters). - * The others take one argument: ignore trailing spaces. - */ + // :bdelete, :bwipeout and :bunload take several arguments, separated + // by spaces: find next space (skipping over escaped characters). + // The others take one argument: ignore trailing spaces. if (ea.cmdidx == CMD_bdelete || ea.cmdidx == CMD_bwipeout || ea.cmdidx == CMD_bunload) { p = skiptowhite_esc(ea.arg); @@ -2336,14 +2303,10 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter // 7. Execute the command. if (IS_USER_CMDIDX(ea.cmdidx)) { - /* - * Execute a user-defined command. - */ + // Execute a user-defined command. do_ucmd(&ea, false); } else { - /* - * Call the function to execute the command. - */ + // Call the function to execute the command. ea.errmsg = NULL; (cmdnames[ea.cmdidx].cmd_func)(&ea); if (ea.errmsg != NULL) { @@ -2351,13 +2314,11 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter } } - /* - * If the command just executed called do_cmdline(), any throw or ":return" - * or ":finish" encountered there must also check the cstack of the still - * active do_cmdline() that called this do_one_cmd(). Rethrow an uncaught - * exception, or reanimate a returned function or finished script file and - * return or finish it again. - */ + // If the command just executed called do_cmdline(), any throw or ":return" + // or ":finish" encountered there must also check the cstack of the still + // active do_cmdline() that called this do_one_cmd(). Rethrow an uncaught + // exception, or reanimate a returned function or finished script file and + // return or finish it again. if (need_rethrow) { do_throw(cstack); } else if (check_cstack) { -- cgit From dc24cb668c154df526584e8937e87d479e255aed Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 27 Jul 2022 14:29:48 +0100 Subject: refactor(cmd): hoist out some code into functions --- src/nvim/ex_docmd.c | 342 ++++++++++++++++++++++++++-------------------------- 1 file changed, 172 insertions(+), 170 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 2343978ed8..bf8153637c 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -1563,6 +1563,28 @@ err: return false; } +static void execute_cmd0(int *retv, exarg_T *eap, char **errormsg, bool preview) +{ + // Execute the command + if (IS_USER_CMDIDX(eap->cmdidx)) { + // Execute a user-defined command. + *retv = do_ucmd(eap, preview); + } else { + // Call the function to execute the command or the preview callback. + eap->errmsg = NULL; + + if (preview) { + *retv = (cmdnames[eap->cmdidx].cmd_preview_func)(eap, cmdpreview_get_ns(), + cmdpreview_get_bufnr()); + } else { + (cmdnames[eap->cmdidx].cmd_func)(eap); + } + if (eap->errmsg != NULL) { + *errormsg = _(eap->errmsg); + } + } +} + /// Execute an Ex command using parsed command line information. /// Does not do any validation of the Ex command arguments. /// @@ -1675,23 +1697,7 @@ int execute_cmd(exarg_T *eap, CmdParseInfo *cmdinfo, bool preview) } // Execute the command - if (IS_USER_CMDIDX(eap->cmdidx)) { - // Execute a user-defined command. - retv = do_ucmd(eap, preview); - } else { - // Call the function to execute the command or the preview callback. - eap->errmsg = NULL; - - if (preview) { - retv = (cmdnames[eap->cmdidx].cmd_preview_func)(eap, cmdpreview_get_ns(), - cmdpreview_get_bufnr()); - } else { - (cmdnames[eap->cmdidx].cmd_func)(eap); - } - if (eap->errmsg != NULL) { - errormsg = _(eap->errmsg); - } - } + execute_cmd0(&retv, eap, &errormsg, preview); end: if (errormsg != NULL && *errormsg != NUL) { @@ -1704,6 +1710,142 @@ end: #undef ERROR } +static void profile_cmd(const exarg_T *eap, cstack_T *cstack, LineGetter fgetline, void *cookie) +{ + // Count this line for profiling if skip is TRUE. + if (do_profiling == PROF_YES + && (!eap->skip || cstack->cs_idx == 0 + || (cstack->cs_idx > 0 + && (cstack->cs_flags[cstack->cs_idx - 1] & CSF_ACTIVE)))) { + int skip = did_emsg || got_int || current_exception; + + if (eap->cmdidx == CMD_catch) { + skip = !skip && !(cstack->cs_idx >= 0 + && (cstack->cs_flags[cstack->cs_idx] & CSF_THROWN) + && !(cstack->cs_flags[cstack->cs_idx] & CSF_CAUGHT)); + } else if (eap->cmdidx == CMD_else || eap->cmdidx == CMD_elseif) { + skip = skip || !(cstack->cs_idx >= 0 + && !(cstack->cs_flags[cstack->cs_idx] + & (CSF_ACTIVE | CSF_TRUE))); + } else if (eap->cmdidx == CMD_finally) { + skip = false; + } else if (eap->cmdidx != CMD_endif + && eap->cmdidx != CMD_endfor + && eap->cmdidx != CMD_endtry + && eap->cmdidx != CMD_endwhile) { + skip = eap->skip; + } + + if (!skip) { + if (getline_equal(fgetline, cookie, get_func_line)) { + func_line_exec(getline_cookie(fgetline, cookie)); + } else if (getline_equal(fgetline, cookie, getsourceline)) { + script_line_exec(); + } + } + } +} + +static bool skip_cmd(const exarg_T *eap) +{ + // Skip the command when it's not going to be executed. + // The commands like :if, :endif, etc. always need to be executed. + // Also make an exception for commands that handle a trailing command + // themselves. + if (eap->skip) { + switch (eap->cmdidx) { + // commands that need evaluation + case CMD_while: + case CMD_endwhile: + case CMD_for: + case CMD_endfor: + case CMD_if: + case CMD_elseif: + case CMD_else: + case CMD_endif: + case CMD_try: + case CMD_catch: + case CMD_finally: + case CMD_endtry: + case CMD_function: + break; + + // Commands that handle '|' themselves. Check: A command should + // either have the EX_TRLBAR flag, appear in this list or appear in + // the list at ":help :bar". + case CMD_aboveleft: + case CMD_and: + case CMD_belowright: + case CMD_botright: + case CMD_browse: + case CMD_call: + case CMD_confirm: + case CMD_const: + case CMD_delfunction: + case CMD_djump: + case CMD_dlist: + case CMD_dsearch: + case CMD_dsplit: + case CMD_echo: + case CMD_echoerr: + case CMD_echomsg: + case CMD_echon: + case CMD_eval: + case CMD_execute: + case CMD_filter: + case CMD_help: + case CMD_hide: + case CMD_ijump: + case CMD_ilist: + case CMD_isearch: + case CMD_isplit: + case CMD_keepalt: + case CMD_keepjumps: + case CMD_keepmarks: + case CMD_keeppatterns: + case CMD_leftabove: + case CMD_let: + case CMD_lockmarks: + case CMD_lockvar: + case CMD_lua: + case CMD_match: + case CMD_mzscheme: + case CMD_noautocmd: + case CMD_noswapfile: + case CMD_perl: + case CMD_psearch: + case CMD_python: + case CMD_py3: + case CMD_python3: + case CMD_pythonx: + case CMD_pyx: + case CMD_return: + case CMD_rightbelow: + case CMD_ruby: + case CMD_silent: + case CMD_smagic: + case CMD_snomagic: + case CMD_substitute: + case CMD_syntax: + case CMD_tab: + case CMD_tcl: + case CMD_throw: + case CMD_tilde: + case CMD_topleft: + case CMD_unlet: + case CMD_unlockvar: + case CMD_verbose: + case CMD_vertical: + case CMD_wincmd: + break; + + default: + return true; + } + } + return false; +} + /// Execute one Ex command. /// /// If 'sourcing' is TRUE, the command will be included in the error message. @@ -1786,38 +1928,7 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter } char *p = find_ex_command(&ea, NULL); - // Count this line for profiling if skip is TRUE. - if (do_profiling == PROF_YES - && (!ea.skip || cstack->cs_idx == 0 - || (cstack->cs_idx > 0 - && (cstack->cs_flags[cstack->cs_idx - 1] & CSF_ACTIVE)))) { - int skip = did_emsg || got_int || current_exception; - - if (ea.cmdidx == CMD_catch) { - skip = !skip && !(cstack->cs_idx >= 0 - && (cstack->cs_flags[cstack->cs_idx] & CSF_THROWN) - && !(cstack->cs_flags[cstack->cs_idx] & CSF_CAUGHT)); - } else if (ea.cmdidx == CMD_else || ea.cmdidx == CMD_elseif) { - skip = skip || !(cstack->cs_idx >= 0 - && !(cstack->cs_flags[cstack->cs_idx] - & (CSF_ACTIVE | CSF_TRUE))); - } else if (ea.cmdidx == CMD_finally) { - skip = false; - } else if (ea.cmdidx != CMD_endif - && ea.cmdidx != CMD_endfor - && ea.cmdidx != CMD_endtry - && ea.cmdidx != CMD_endwhile) { - skip = ea.skip; - } - - if (!skip) { - if (getline_equal(fgetline, cookie, get_func_line)) { - func_line_exec(getline_cookie(fgetline, cookie)); - } else if (getline_equal(fgetline, cookie, getsourceline)) { - script_line_exec(); - } - } - } + profile_cmd(&ea, cstack, fgetline, cookie); // May go to debug mode. If this happens and the ">quit" debug command is // used, throw an interrupt exception and skip the next command. @@ -1934,12 +2045,12 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter const int ni = is_cmd_ni(ea.cmdidx); // Forced commands. - if (*p == '!' && ea.cmdidx != CMD_substitute - && ea.cmdidx != CMD_smagic && ea.cmdidx != CMD_snomagic) { + ea.forceit = *p == '!' + && ea.cmdidx != CMD_substitute + && ea.cmdidx != CMD_smagic + && ea.cmdidx != CMD_snomagic; + if (ea.forceit) { p++; - ea.forceit = true; - } else { - ea.forceit = false; } // 6. Parse arguments. Then check for errors. @@ -2048,11 +2159,7 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter // Skip to start of argument. // Don't do this for the ":!" command, because ":!! -l" needs the space. - if (ea.cmdidx == CMD_bang) { - ea.arg = p; - } else { - ea.arg = skipwhite(p); - } + ea.arg = ea.cmdidx == CMD_bang ? p : skipwhite(p); // ":file" cannot be run with an argument when "curbuf->b_ro_locked" is set if (ea.cmdidx == CMD_file && *ea.arg != NUL && curbuf_locked()) { @@ -2082,9 +2189,7 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter ++ea.arg; ea.usefilter = TRUE; } - } - - if (ea.cmdidx == CMD_read) { + } else if (ea.cmdidx == CMD_read) { if (ea.forceit) { ea.usefilter = TRUE; // :r! filter if ea.forceit ea.forceit = FALSE; @@ -2092,9 +2197,7 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter ++ea.arg; ea.usefilter = TRUE; } - } - - if (ea.cmdidx == CMD_lshift || ea.cmdidx == CMD_rshift) { + } else if (ea.cmdidx == CMD_lshift || ea.cmdidx == CMD_rshift) { ea.amount = 1; while (*ea.arg == *ea.cmd) { // count number of '>' or '<' ea.arg++; @@ -2164,100 +2267,8 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter goto doend; } - // Skip the command when it's not going to be executed. - // The commands like :if, :endif, etc. always need to be executed. - // Also make an exception for commands that handle a trailing command - // themselves. - if (ea.skip) { - switch (ea.cmdidx) { - // commands that need evaluation - case CMD_while: - case CMD_endwhile: - case CMD_for: - case CMD_endfor: - case CMD_if: - case CMD_elseif: - case CMD_else: - case CMD_endif: - case CMD_try: - case CMD_catch: - case CMD_finally: - case CMD_endtry: - case CMD_function: - break; - - // Commands that handle '|' themselves. Check: A command should - // either have the EX_TRLBAR flag, appear in this list or appear in - // the list at ":help :bar". - case CMD_aboveleft: - case CMD_and: - case CMD_belowright: - case CMD_botright: - case CMD_browse: - case CMD_call: - case CMD_confirm: - case CMD_const: - case CMD_delfunction: - case CMD_djump: - case CMD_dlist: - case CMD_dsearch: - case CMD_dsplit: - case CMD_echo: - case CMD_echoerr: - case CMD_echomsg: - case CMD_echon: - case CMD_eval: - case CMD_execute: - case CMD_filter: - case CMD_help: - case CMD_hide: - case CMD_ijump: - case CMD_ilist: - case CMD_isearch: - case CMD_isplit: - case CMD_keepalt: - case CMD_keepjumps: - case CMD_keepmarks: - case CMD_keeppatterns: - case CMD_leftabove: - case CMD_let: - case CMD_lockmarks: - case CMD_lockvar: - case CMD_lua: - case CMD_match: - case CMD_mzscheme: - case CMD_noautocmd: - case CMD_noswapfile: - case CMD_perl: - case CMD_psearch: - case CMD_python: - case CMD_py3: - case CMD_python3: - case CMD_pythonx: - case CMD_pyx: - case CMD_return: - case CMD_rightbelow: - case CMD_ruby: - case CMD_silent: - case CMD_smagic: - case CMD_snomagic: - case CMD_substitute: - case CMD_syntax: - case CMD_tab: - case CMD_tcl: - case CMD_throw: - case CMD_tilde: - case CMD_topleft: - case CMD_unlet: - case CMD_unlockvar: - case CMD_verbose: - case CMD_vertical: - case CMD_wincmd: - break; - - default: - goto doend; - } + if (skip_cmd(&ea)) { + goto doend; } if (ea.argt & EX_XFILE) { @@ -2302,17 +2313,8 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter } // 7. Execute the command. - if (IS_USER_CMDIDX(ea.cmdidx)) { - // Execute a user-defined command. - do_ucmd(&ea, false); - } else { - // Call the function to execute the command. - ea.errmsg = NULL; - (cmdnames[ea.cmdidx].cmd_func)(&ea); - if (ea.errmsg != NULL) { - errormsg = _(ea.errmsg); - } - } + int retv = 0; + execute_cmd0(&retv, &ea, &errormsg, false); // If the command just executed called do_cmdline(), any throw or ":return" // or ":finish" encountered there must also check the cstack of the still -- cgit From dc2745e9eac08d4537c409a8700c37bc9d42d6de Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 27 Jul 2022 15:42:56 +0100 Subject: refactor(cmd): unify execute_cmd with do_one_cmd --- src/nvim/api/command.c | 5 ++ src/nvim/ex_cmds_defs.h | 56 ++++++++-------- src/nvim/ex_docmd.c | 165 ++++++++++++++++++++---------------------------- 3 files changed, 102 insertions(+), 124 deletions(-) (limited to 'src') diff --git a/src/nvim/api/command.c b/src/nvim/api/command.c index 33efa6b326..5ab0beec9d 100644 --- a/src/nvim/api/command.c +++ b/src/nvim/api/command.c @@ -505,6 +505,11 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error OBJ_TO_BOOL(cmdinfo.magic.file, magic.file, ea.argt & EX_XFILE, "'magic.file'"); OBJ_TO_BOOL(cmdinfo.magic.bar, magic.bar, ea.argt & EX_TRLBAR, "'magic.bar'"); + if (cmdinfo.magic.file) { + ea.argt |= EX_XFILE; + } else { + ea.argt &= ~EX_XFILE; + } } else { cmdinfo.magic.file = ea.argt & EX_XFILE; cmdinfo.magic.bar = ea.argt & EX_TRLBAR; diff --git a/src/nvim/ex_cmds_defs.h b/src/nvim/ex_cmds_defs.h index 052926fa1f..e80e47bcff 100644 --- a/src/nvim/ex_cmds_defs.h +++ b/src/nvim/ex_cmds_defs.h @@ -37,34 +37,34 @@ // 4. Add documentation in ../doc/xxx.txt. Add a tag for both the short and // long name of the command. -#define EX_RANGE 0x001 // allow a linespecs -#define EX_BANG 0x002 // allow a ! after the command name -#define EX_EXTRA 0x004 // allow extra args after command name -#define EX_XFILE 0x008 // expand wildcards in extra part -#define EX_NOSPC 0x010 // no spaces allowed in the extra part -#define EX_DFLALL 0x020 // default file range is 1,$ -#define EX_WHOLEFOLD 0x040 // extend range to include whole fold also - // when less than two numbers given -#define EX_NEEDARG 0x080 // argument required -#define EX_TRLBAR 0x100 // check for trailing vertical bar -#define EX_REGSTR 0x200 // allow "x for register designation -#define EX_COUNT 0x400 // allow count in argument, after command -#define EX_NOTRLCOM 0x800 // no trailing comment allowed -#define EX_ZEROR 0x1000 // zero line number allowed -#define EX_CTRLV 0x2000 // do not remove CTRL-V from argument -#define EX_CMDARG 0x4000 // allow "+command" argument -#define EX_BUFNAME 0x8000 // accepts buffer name -#define EX_BUFUNL 0x10000 // accepts unlisted buffer too -#define EX_ARGOPT 0x20000 // allow "++opt=val" argument -#define EX_SBOXOK 0x40000 // allowed in the sandbox -#define EX_CMDWIN 0x80000 // allowed in cmdline window -#define EX_MODIFY 0x100000 // forbidden in non-'modifiable' buffer -#define EX_FLAGS 0x200000 // allow flags after count in argument -#define EX_LOCK_OK 0x1000000 // command can be executed when textlock is - // set; when missing disallows editing another - // buffer when current buffer is locked -#define EX_KEEPSCRIPT 0x4000000 // keep sctx of where command was invoked -#define EX_PREVIEW 0x8000000 // allow incremental command preview +#define EX_RANGE 0x001u // allow a linespecs +#define EX_BANG 0x002u // allow a ! after the command name +#define EX_EXTRA 0x004u // allow extra args after command name +#define EX_XFILE 0x008u // expand wildcards in extra part +#define EX_NOSPC 0x010u // no spaces allowed in the extra part +#define EX_DFLALL 0x020u // default file range is 1,$ +#define EX_WHOLEFOLD 0x040u // extend range to include whole fold also + // when less than two numbers given +#define EX_NEEDARG 0x080u // argument required +#define EX_TRLBAR 0x100u // check for trailing vertical bar +#define EX_REGSTR 0x200u // allow "x for register designation +#define EX_COUNT 0x400u // allow count in argument, after command +#define EX_NOTRLCOM 0x800u // no trailing comment allowed +#define EX_ZEROR 0x1000u // zero line number allowed +#define EX_CTRLV 0x2000u // do not remove CTRL-V from argument +#define EX_CMDARG 0x4000u // allow "+command" argument +#define EX_BUFNAME 0x8000u // accepts buffer name +#define EX_BUFUNL 0x10000u // accepts unlisted buffer too +#define EX_ARGOPT 0x20000u // allow "++opt=val" argument +#define EX_SBOXOK 0x40000u // allowed in the sandbox +#define EX_CMDWIN 0x80000u // allowed in cmdline window +#define EX_MODIFY 0x100000u // forbidden in non-'modifiable' buffer +#define EX_FLAGS 0x200000u // allow flags after count in argument +#define EX_LOCK_OK 0x1000000u // command can be executed when textlock is + // set; when missing disallows editing another + // buffer when current buffer is locked +#define EX_KEEPSCRIPT 0x4000000u // keep sctx of where command was invoked +#define EX_PREVIEW 0x8000000u // allow incremental command preview #define EX_FILES (EX_XFILE | EX_EXTRA) // multiple extra files allowed #define EX_FILE1 (EX_FILES | EX_NOSPC) // 1 file, defaults to current file #define EX_WORD1 (EX_EXTRA | EX_NOSPC) // one extra word allowed diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index bf8153637c..31314fd8b9 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -1563,8 +1563,70 @@ err: return false; } -static void execute_cmd0(int *retv, exarg_T *eap, char **errormsg, bool preview) +static int execute_cmd0(int *retv, exarg_T *eap, char **errormsg, bool preview) { + // If filename expansion is enabled, expand filenames + if (eap->argt & EX_XFILE) { + if (expand_filename(eap, eap->cmdlinep, errormsg) == FAIL) { + return FAIL; + } + } + + // Accept buffer name. Cannot be used at the same time with a buffer + // number. Don't do this for a user command. + if ((eap->argt & EX_BUFNAME) && *eap->arg != NUL && eap->addr_count == 0 + && !IS_USER_CMDIDX(eap->cmdidx)) { + if (eap->args == NULL) { + // If argument positions are not specified, search the argument for the buffer name. + // :bdelete, :bwipeout and :bunload take several arguments, separated by spaces: + // find next space (skipping over escaped characters). + // The others take one argument: ignore trailing spaces. + char *p; + + if (eap->cmdidx == CMD_bdelete || eap->cmdidx == CMD_bwipeout + || eap->cmdidx == CMD_bunload) { + p = skiptowhite_esc(eap->arg); + } else { + p = eap->arg + STRLEN(eap->arg); + while (p > eap->arg && ascii_iswhite(p[-1])) { + p--; + } + } + eap->line2 = buflist_findpat(eap->arg, p, (eap->argt & EX_BUFUNL) != 0, + false, false); + eap->addr_count = 1; + eap->arg = skipwhite(p); + } else { + // If argument positions are specified, just use the first argument + eap->line2 = buflist_findpat(eap->args[0], + eap->args[0] + eap->arglens[0], + (eap->argt & EX_BUFUNL) != 0, false, false); + eap->addr_count = 1; + // Shift each argument by 1 + for (size_t i = 0; i < eap->argc - 1; i++) { + eap->args[i] = eap->args[i + 1]; + } + // Make the last argument point to the NUL terminator at the end of string + eap->args[eap->argc - 1] = eap->args[eap->argc - 1] + eap->arglens[eap->argc - 1]; + eap->argc -= 1; + + eap->arg = eap->args[0]; + } + if (eap->line2 < 0) { // failed + return FAIL; + } + } + + // The :try command saves the emsg_silent flag, reset it here when + // ":silent! try" was used, it should only apply to :try itself. + if (eap->cmdidx == CMD_try && cmdmod.cmod_did_esilent > 0) { + emsg_silent -= cmdmod.cmod_did_esilent; + if (emsg_silent < 0) { + emsg_silent = 0; + } + cmdmod.cmod_did_esilent = 0; + } + // Execute the command if (IS_USER_CMDIDX(eap->cmdidx)) { // Execute a user-defined command. @@ -1583,6 +1645,8 @@ static void execute_cmd0(int *retv, exarg_T *eap, char **errormsg, bool preview) *errormsg = _(eap->errmsg); } } + + return OK; } /// Execute an Ex command using parsed command line information. @@ -1644,58 +1708,6 @@ int execute_cmd(exarg_T *eap, CmdParseInfo *cmdinfo, bool preview) (void)hasFolding(eap->line2, NULL, &eap->line2); } - // If filename expansion is enabled, expand filenames - if (cmdinfo->magic.file) { - if (expand_filename(eap, eap->cmdlinep, &errormsg) == FAIL) { - goto end; - } - } - - // Accept buffer name. Cannot be used at the same time with a buffer - // number. Don't do this for a user command. - if ((eap->argt & EX_BUFNAME) && *eap->arg != NUL && eap->addr_count == 0 - && !IS_USER_CMDIDX(eap->cmdidx)) { - if (eap->args == NULL) { - // If argument positions are not specified, search the argument for the buffer name. - // :bdelete, :bwipeout and :bunload take several arguments, separated by spaces: - // find next space (skipping over escaped characters). - // The others take one argument: ignore trailing spaces. - char *p; - - if (eap->cmdidx == CMD_bdelete || eap->cmdidx == CMD_bwipeout - || eap->cmdidx == CMD_bunload) { - p = skiptowhite_esc(eap->arg); - } else { - p = eap->arg + STRLEN(eap->arg); - while (p > eap->arg && ascii_iswhite(p[-1])) { - p--; - } - } - eap->line2 = buflist_findpat(eap->arg, p, (eap->argt & EX_BUFUNL) != 0, - false, false); - eap->addr_count = 1; - eap->arg = skipwhite(p); - } else { - // If argument positions are specified, just use the first argument - eap->line2 = buflist_findpat(eap->args[0], - eap->args[0] + eap->arglens[0], - (eap->argt & EX_BUFUNL) != 0, false, false); - eap->addr_count = 1; - // Shift each argument by 1 - for (size_t i = 0; i < eap->argc - 1; i++) { - eap->args[i] = eap->args[i + 1]; - } - // Make the last argument point to the NUL terminator at the end of string - eap->args[eap->argc - 1] = eap->args[eap->argc - 1] + eap->arglens[eap->argc - 1]; - eap->argc -= 1; - - eap->arg = eap->args[0]; - } - if (eap->line2 < 0) { // failed - goto end; - } - } - // Execute the command execute_cmd0(&retv, eap, &errormsg, preview); @@ -2271,50 +2283,11 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter goto doend; } - if (ea.argt & EX_XFILE) { - if (expand_filename(&ea, cmdlinep, &errormsg) == FAIL) { - goto doend; - } - } - - // Accept buffer name. Cannot be used at the same time with a buffer - // number. Don't do this for a user command. - if ((ea.argt & EX_BUFNAME) && *ea.arg != NUL && ea.addr_count == 0 - && !IS_USER_CMDIDX(ea.cmdidx)) { - // :bdelete, :bwipeout and :bunload take several arguments, separated - // by spaces: find next space (skipping over escaped characters). - // The others take one argument: ignore trailing spaces. - if (ea.cmdidx == CMD_bdelete || ea.cmdidx == CMD_bwipeout - || ea.cmdidx == CMD_bunload) { - p = skiptowhite_esc(ea.arg); - } else { - p = ea.arg + STRLEN(ea.arg); - while (p > ea.arg && ascii_iswhite(p[-1])) { - p--; - } - } - ea.line2 = buflist_findpat(ea.arg, p, (ea.argt & EX_BUFUNL) != 0, - false, false); - if (ea.line2 < 0) { // failed - goto doend; - } - ea.addr_count = 1; - ea.arg = skipwhite(p); - } - - // The :try command saves the emsg_silent flag, reset it here when - // ":silent! try" was used, it should only apply to :try itself. - if (ea.cmdidx == CMD_try && cmdmod.cmod_did_esilent > 0) { - emsg_silent -= cmdmod.cmod_did_esilent; - if (emsg_silent < 0) { - emsg_silent = 0; - } - cmdmod.cmod_did_esilent = 0; - } - // 7. Execute the command. int retv = 0; - execute_cmd0(&retv, &ea, &errormsg, false); + if (execute_cmd0(&retv, &ea, &errormsg, false) == FAIL) { + goto doend; + } // If the command just executed called do_cmdline(), any throw or ":return" // or ":finish" encountered there must also check the cstack of the still @@ -2346,7 +2319,7 @@ doend: STRCPY(IObuff, errormsg); errormsg = (char *)IObuff; } - append_command(*cmdlinep); + append_command(*ea.cmdlinep); } emsg(errormsg); } -- cgit From 6e2c6114f991b3b61887fd9d1b442623b4b03882 Mon Sep 17 00:00:00 2001 From: Dundar Goc Date: Wed, 22 Jun 2022 20:06:15 +0200 Subject: build: replace deprecated cmake features with their modern alternatives - Use DIRECTORY instead of PATH in get_filename_component - Use COMPILE_OPTIONS instead of COMPILE_FLAGS. COMPILE_FLAGS is treated as a single string while COMPILE_OPTIONS is a list, meaning that cmake will take care of any escaping and quoting automatically. --- src/nvim/CMakeLists.txt | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 017883a913..65af69c539 100755 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -225,7 +225,7 @@ foreach(sfile ${NVIM_SOURCES} "${GENERATED_KEYSETS}" "${GENERATED_UI_EVENTS_CLIENT}" ) - get_filename_component(full_d ${sfile} PATH) + get_filename_component(full_d ${sfile} DIRECTORY) file(RELATIVE_PATH d "${CMAKE_CURRENT_LIST_DIR}" "${full_d}") if(${d} MATCHES "^[.][.]|auto/") file(RELATIVE_PATH d "${GENERATED_DIR}" "${full_d}") @@ -597,10 +597,7 @@ set_target_properties( POSITION_INDEPENDENT_CODE ON OUTPUT_NAME ${LIBNVIM_NAME} ) -set_property( - TARGET libnvim - APPEND_STRING PROPERTY COMPILE_FLAGS " -DMAKE_LIB " -) +target_compile_options(libnvim PRIVATE -DMAKE_LIB) if(NOT LUAJIT_FOUND) message(STATUS "luajit not found, skipping nvim-test (unit tests) target") @@ -626,10 +623,7 @@ else() PROPERTIES POSITION_INDEPENDENT_CODE ON ) - set_property( - TARGET nvim-test - APPEND_STRING PROPERTY COMPILE_FLAGS " -DUNIT_TESTING " - ) + target_compile_options(nvim-test PRIVATE -DUNIT_TESTING) endif() if(CLANG_ASAN_UBSAN) @@ -664,7 +658,7 @@ elseif(CLANG_TSAN) endif() function(get_test_target prefix sfile relative_path_var target_var) - get_filename_component(full_d "${sfile}" PATH) + get_filename_component(full_d "${sfile}" DIRECTORY) file(RELATIVE_PATH d "${PROJECT_SOURCE_DIR}/src/nvim" "${full_d}") if(d MATCHES "^[.][.]") file(RELATIVE_PATH d "${GENERATED_DIR}" "${full_d}") -- cgit From d3bfc03c5d88466d2644cfaddcce48805b10ebca Mon Sep 17 00:00:00 2001 From: Dundar Goc Date: Fri, 1 Jul 2022 17:06:29 +0200 Subject: build: remove InstallClintErrors.cmake Replace its functionality by copying the entire directory where the reports are instead. --- src/nvim/CMakeLists.txt | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 65af69c539..b743e9923f 100755 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -77,7 +77,7 @@ set(LINT_SUPPRESSES_ROOT ${PROJECT_BINARY_DIR}/errors) set(LINT_SUPPRESSES_URL "${LINT_SUPPRESS_URL_BASE}/errors.tar.gz") set(LINT_SUPPRESSES_ARCHIVE ${LINT_SUPPRESSES_ROOT}/errors.tar.gz) set(LINT_SUPPRESSES_TOUCH_FILE "${TOUCHES_DIR}/unpacked-clint-errors-archive") -set(LINT_SUPPRESSES_INSTALL_SCRIPT "${PROJECT_SOURCE_DIR}/cmake/InstallClintErrors.cmake") +set(CLINT_REPORT_PATH ${LINT_SUPPRESSES_ROOT}/src/home/runner/work/doc/doc/gh-pages/reports/clint) glob_wrapper(UNICODE_FILES ${UNICODE_DIR}/*.txt) glob_wrapper(API_HEADERS api/*.h) @@ -724,13 +724,9 @@ add_custom_command( OUTPUT ${LINT_SUPPRESSES_TOUCH_FILE} WORKING_DIRECTORY ${LINT_SUPPRESSES_ROOT}/src COMMAND ${CMAKE_COMMAND} -E tar xfz ${LINT_SUPPRESSES_ARCHIVE} - COMMAND - ${CMAKE_COMMAND} - -DTARGET=${LINT_SUPPRESSES_ROOT} - -P ${LINT_SUPPRESSES_INSTALL_SCRIPT} + COMMAND ${CMAKE_COMMAND} -E copy_directory ${CLINT_REPORT_PATH} "${LINT_SUPPRESSES_ROOT}" COMMAND ${CMAKE_COMMAND} -E touch ${LINT_SUPPRESSES_TOUCH_FILE} - DEPENDS - ${LINT_SUPPRESSES_ARCHIVE} ${LINT_SUPPRESSES_INSTALL_SCRIPT} + DEPENDS ${LINT_SUPPRESSES_ARCHIVE} ) add_download(${LINT_SUPPRESS_FILE} ${LINT_SUPPRESS_URL} off) -- cgit From c57e133e50b9f3ccd9a3d73f4e7e3e7281797000 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 3 Aug 2022 19:25:03 +0800 Subject: fix(ui): set redraw_cmdline when setting window height (#19630) --- src/nvim/window.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/nvim/window.c b/src/nvim/window.c index c7f038850e..b737215616 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -5518,6 +5518,7 @@ void win_setheight_win(int height, win_T *win) msg_row = row; msg_col = 0; redraw_all_later(NOT_VALID); + redraw_cmdline = true; } } -- cgit From 3df8d9b8c56a7f0af0f7590b11831bd96ead92f1 Mon Sep 17 00:00:00 2001 From: ii14 <59243201+ii14@users.noreply.github.com> Date: Wed, 3 Aug 2022 14:41:17 +0200 Subject: feat(lua): print source locations of lua callbacks (#19597) Co-authored-by: ii14 --- src/nvim/api/command.c | 5 ++--- src/nvim/autocmd.c | 9 ++++++++- src/nvim/eval/typval.c | 11 ++++++----- src/nvim/ex_docmd.c | 12 +++++++++++- src/nvim/lua/executor.c | 31 +++++++++++++++++++++++++++++++ src/nvim/mapping.c | 11 ++++------- src/nvim/os/env.c | 2 +- 7 files changed, 63 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/nvim/api/command.c b/src/nvim/api/command.c index 5ab0beec9d..04242932c9 100644 --- a/src/nvim/api/command.c +++ b/src/nvim/api/command.c @@ -944,7 +944,7 @@ void create_user_command(String name, Object command, Dict(user_command) *opts, cmd_addr_T addr_type_arg = ADDR_NONE; int compl = EXPAND_NOTHING; char *compl_arg = NULL; - char *rep = NULL; + const char *rep = NULL; LuaRef luaref = LUA_NOREF; LuaRef compl_luaref = LUA_NOREF; LuaRef preview_luaref = LUA_NOREF; @@ -1116,8 +1116,7 @@ void create_user_command(String name, Object command, Dict(user_command) *opts, if (opts->desc.type == kObjectTypeString) { rep = opts->desc.data.string.data; } else { - snprintf((char *)IObuff, IOSIZE, "", luaref); - rep = (char *)IObuff; + rep = ""; } break; case kObjectTypeString: diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 2b4c9c5b9c..bbb044fba3 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -196,9 +196,16 @@ static void aupat_show(AutoPat *ap, event_T event, int previous_group) if (ac->desc != NULL) { size_t msglen = 100; char *msg = (char *)xmallocz(msglen); - snprintf(msg, msglen, "%s [%s]", exec_to_string, ac->desc); + if (ac->exec.type == CALLABLE_CB) { + msg_puts_attr(exec_to_string, HL_ATTR(HLF_8)); + snprintf(msg, msglen, " [%s]", ac->desc); + } else { + snprintf(msg, msglen, "%s [%s]", exec_to_string, ac->desc); + } msg_outtrans(msg); XFREE_CLEAR(msg); + } else if (ac->exec.type == CALLABLE_CB) { + msg_puts_attr(exec_to_string, HL_ATTR(HLF_8)); } else { msg_outtrans(exec_to_string); } diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index fd57b45e86..ff1808ed91 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1657,13 +1657,14 @@ void callback_copy(Callback *dest, Callback *src) /// Generate a string description of a callback char *callback_to_string(Callback *cb) { - size_t msglen = 100; + if (cb->type == kCallbackLua) { + return nlua_funcref_str(cb->data.luaref); + } + + const size_t msglen = 100; char *msg = (char *)xmallocz(msglen); switch (cb->type) { - case kCallbackLua: - snprintf(msg, msglen, "", cb->data.luaref); - break; case kCallbackFuncref: // TODO(tjdevries): Is this enough space for this? snprintf(msg, msglen, "", cb->data.funcref); @@ -1672,7 +1673,7 @@ char *callback_to_string(Callback *cb) snprintf(msg, msglen, "", cb->data.partial->pt_name); break; default: - snprintf(msg, msglen, "%s", ""); + *msg = '\0'; break; } return msg; diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 31314fd8b9..e3d61ef7e2 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -5440,7 +5440,7 @@ char *uc_validate_name(char *name) /// This function takes ownership of compl_arg, compl_luaref, and luaref. /// /// @return OK if the command is created, FAIL otherwise. -int uc_add_command(char *name, size_t name_len, char *rep, uint32_t argt, long def, int flags, +int uc_add_command(char *name, size_t name_len, const char *rep, uint32_t argt, long def, int flags, int compl, char *compl_arg, LuaRef compl_luaref, LuaRef preview_luaref, cmd_addr_T addr_type, LuaRef luaref, bool force) FUNC_ATTR_NONNULL_ARG(1, 3) @@ -5762,6 +5762,16 @@ static void uc_list(char *name, size_t name_len) IObuff[len] = '\0'; msg_outtrans((char *)IObuff); + if (cmd->uc_luaref != LUA_NOREF) { + char *fn = nlua_funcref_str(cmd->uc_luaref); + msg_puts_attr(fn, HL_ATTR(HLF_8)); + xfree(fn); + // put the description on a new line + if (*cmd->uc_rep != NUL) { + msg_puts("\n "); + } + } + msg_outtrans_special(cmd->uc_rep, false, name_len == 0 ? Columns - 47 : 0); if (p_verbose > 0) { diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 17157ccdc2..197a209e97 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -2076,3 +2076,34 @@ int nlua_do_ucmd(ucmd_T *cmd, exarg_T *eap, bool preview) return retv; } + +/// String representation of a Lua function reference +/// +/// @return Allocated string +char *nlua_funcref_str(LuaRef ref) +{ + lua_State *const lstate = global_lstate; + StringBuilder str = KV_INITIAL_VALUE; + kv_resize(str, 16); + + if (!lua_checkstack(lstate, 1)) { + goto plain; + } + nlua_pushref(lstate, ref); + if (!lua_isfunction(lstate, -1)) { + lua_pop(lstate, 1); + goto plain; + } + + lua_Debug ar; + if (lua_getinfo(lstate, ">S", &ar) && *ar.source == '@' && ar.linedefined >= 0) { + char *src = home_replace_save(NULL, ar.source + 1); + kv_printf(str, "", ref, src, ar.linedefined); + xfree(src); + return str.items; + } + +plain: + kv_printf(str, "", ref); + return str.items; +} diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c index 1797bb0365..342b1b0d47 100644 --- a/src/nvim/mapping.c +++ b/src/nvim/mapping.c @@ -196,9 +196,9 @@ static void showmap(mapblock_T *mp, bool local) // Use false below if we only want things like to show up as such on // the rhs, and not M-x etc, true gets both -- webb if (mp->m_luaref != LUA_NOREF) { - char msg[100]; - snprintf(msg, sizeof(msg), "", mp->m_luaref); - msg_puts_attr(msg, HL_ATTR(HLF_8)); + char *str = nlua_funcref_str(mp->m_luaref); + msg_puts_attr(str, HL_ATTR(HLF_8)); + xfree(str); } else if (mp->m_str[0] == NUL) { msg_puts_attr("", HL_ATTR(HLF_8)); } else { @@ -2110,10 +2110,7 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) rettv->vval.v_string = str2special_save((char *)rhs, false, false); } } else if (rhs_lua != LUA_NOREF) { - size_t msglen = 100; - char *msg = (char *)xmalloc(msglen); - snprintf(msg, msglen, "", mp->m_luaref); - rettv->vval.v_string = msg; + rettv->vval.v_string = nlua_funcref_str(mp->m_luaref); } } else { tv_dict_alloc_ret(rettv); diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index 9c93057fe7..eaa56ffe63 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -1143,7 +1143,7 @@ size_t home_replace(const buf_T *const buf, const char *src, char *const dst, si /// Like home_replace, store the replaced string in allocated memory. /// @param buf When not NULL, check for help files /// @param src Input file name -char *home_replace_save(buf_T *buf, char *src) +char *home_replace_save(buf_T *buf, const char *src) FUNC_ATTR_NONNULL_RET { size_t len = 3; // space for "~/" and trailing NUL -- cgit From 0a29267514c57c438d68a3d5599bfada41363b24 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 3 Aug 2022 21:50:14 +0800 Subject: fix(completion): remove wrong FUNC_ATTR_NONNULL_ALL (#19627) --- src/nvim/insexpand.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c index 2fc8f1dadc..a64d8e8f00 100644 --- a/src/nvim/insexpand.c +++ b/src/nvim/insexpand.c @@ -825,7 +825,6 @@ static void ins_compl_longest_match(compl_T *match) /// Add an array of matches to the list of matches. /// Frees matches[]. static void ins_compl_add_matches(int num_matches, char **matches, int icase) - FUNC_ATTR_NONNULL_ALL { int add_r = OK; Direction dir = compl_direction; -- cgit From 69299380ca501558c1d844fc90075cfb7de17a15 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 4 Aug 2022 14:45:05 +0800 Subject: fix(menu): make :menu still print header when there are no menus --- src/nvim/menu.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/nvim/menu.c b/src/nvim/menu.c index febb66081a..15e1fbe148 100644 --- a/src/nvim/menu.c +++ b/src/nvim/menu.c @@ -810,17 +810,23 @@ static vimmenu_T *find_menu(vimmenu_T *menu, char *name, int modes) /// Show the mapping associated with a menu item or hierarchy in a sub-menu. static int show_menus(char *const path_name, int modes) { - // First, find the (sub)menu with the given name - vimmenu_T *menu = find_menu(*get_root_menu(path_name), path_name, modes); - if (!menu) { - return FAIL; + vimmenu_T *menu = *get_root_menu(path_name); + if (menu != NULL) { + // First, find the (sub)menu with the given name + menu = find_menu(menu, path_name, modes); + if (menu == NULL) { + return FAIL; + } } + // When there are no menus at all, the title still needs to be shown. // Now we have found the matching menu, and we list the mappings // Highlight title msg_puts_title(_("\n--- Menus ---")); - show_menus_recursive(menu->parent, modes, 0); + if (menu != NULL) { + show_menus_recursive(menu->parent, modes, 0); + } return OK; } -- cgit From 77926b64938fb0c7a9581edbb486b712b29dafac Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 4 Aug 2022 11:11:37 +0800 Subject: vim-patch:8.2.0385: menu functionality insufficiently tested Problem: Menu functionality insufficiently tested. Solution: Add tests. Add menu_info(). (Yegappan Lakshmanan, closes vim/vim#5760) https://github.com/vim/vim/commit/0eabd4dc8ff50658f0ea0e92c7918a42242f6b80 Omit feedkeys() change: even if "L" flag is implemented it will likely use input_enqueue(), which already checks for interrupts. Omit Test_mouse_popup_menu(): already tested in Lua. --- src/nvim/eval.lua | 1 + src/nvim/menu.c | 236 ++++++++++++++++++++++---- src/nvim/testdir/test_menu.vim | 358 ++++++++++++++++++++++++++++++++++++++++ src/nvim/testdir/test_popup.vim | 22 ++- 4 files changed, 574 insertions(+), 43 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index c8eb0334fa..6d8776d08b 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -264,6 +264,7 @@ return { matchstrpos={args={2,4}, base=1}, max={args=1, base=1}, menu_get={args={1, 2}}, + menu_info={args={1, 2}, base=1}, min={args=1, base=1}, mkdir={args={1, 3}, base=1}, mode={args={0, 1}, base=1}, diff --git a/src/nvim/menu.c b/src/nvim/menu.c index 15e1fbe148..6311d5dddc 100644 --- a/src/nvim/menu.c +++ b/src/nvim/menu.c @@ -1167,7 +1167,7 @@ char *menu_name_skip(char *const name) * Return TRUE when "name" matches with menu "menu". The name is compared in * two ways: raw menu name and menu name without '&'. ignore part after a TAB. */ -static bool menu_name_equal(const char *const name, vimmenu_T *const menu) +static bool menu_name_equal(const char *const name, const vimmenu_T *const menu) { if (menu->en_name != NULL && (menu_namecmp(name, menu->en_name) @@ -1262,6 +1262,58 @@ int get_menu_cmd_modes(const char *cmd, bool forceit, int *noremap, int *unmenu) return modes; } +/// Return the string representation of the menu modes. Does the opposite +/// of get_menu_cmd_modes(). +static char *get_menu_mode_str(int modes) +{ + if ((modes & (MENU_INSERT_MODE | MENU_CMDLINE_MODE | MENU_NORMAL_MODE | + MENU_VISUAL_MODE | MENU_SELECT_MODE | MENU_OP_PENDING_MODE)) + == (MENU_INSERT_MODE | MENU_CMDLINE_MODE | MENU_NORMAL_MODE | + MENU_VISUAL_MODE | MENU_SELECT_MODE | MENU_OP_PENDING_MODE)) { + return "a"; + } + if ((modes & (MENU_NORMAL_MODE | MENU_VISUAL_MODE | MENU_SELECT_MODE | + MENU_OP_PENDING_MODE)) + == (MENU_NORMAL_MODE | MENU_VISUAL_MODE | MENU_SELECT_MODE | + MENU_OP_PENDING_MODE)) { + return " "; + } + if ((modes & (MENU_INSERT_MODE | MENU_CMDLINE_MODE)) + == (MENU_INSERT_MODE | MENU_CMDLINE_MODE)) { + return "!"; + } + if ((modes & (MENU_VISUAL_MODE | MENU_SELECT_MODE)) + == (MENU_VISUAL_MODE | MENU_SELECT_MODE)) { + return "v"; + } + if (modes & MENU_VISUAL_MODE) { + return "x"; + } + if (modes & MENU_SELECT_MODE) { + return "s"; + } + if (modes & MENU_OP_PENDING_MODE) { + return "o"; + } + if (modes & MENU_INSERT_MODE) { + return "i"; + } + if (modes & MENU_TERMINAL_MODE) { + return "tl"; + } + if (modes & MENU_CMDLINE_MODE) { + return "c"; + } + if (modes & MENU_NORMAL_MODE) { + return "n"; + } + if (modes & MENU_TIP_MODE) { + return "t"; + } + + return ""; +} + /* * Modify a menu name starting with "PopUp" to include the mode character. * Returns the name in allocated memory. @@ -1553,8 +1605,52 @@ void execute_menu(const exarg_T *eap, vimmenu_T *menu, int mode_idx) } } -// Given a menu descriptor, e.g. "File.New", find it in the menu hierarchy and -// execute it. +/// Lookup a menu by the descriptor name e.g. "File.New" +/// Returns NULL if the menu is not found +static vimmenu_T *menu_getbyname(char *name_arg) + FUNC_ATTR_NONNULL_ALL +{ + char *saved_name = xstrdup(name_arg); + vimmenu_T *menu = *get_root_menu(saved_name); + char *name = saved_name; + bool gave_emsg = false; + while (*name) { + // Find in the menu hierarchy + char *p = menu_name_skip(name); + + while (menu != NULL) { + if (menu_name_equal(name, menu)) { + if (*p == NUL && menu->children != NULL) { + emsg(_("E333: Menu path must lead to a menu item")); + gave_emsg = true; + menu = NULL; + } else if (*p != NUL && menu->children == NULL) { + emsg(_(e_notsubmenu)); + menu = NULL; + } + break; + } + menu = menu->next; + } + if (menu == NULL || *p == NUL) { + break; + } + menu = menu->children; + name = p; + } + xfree(saved_name); + if (menu == NULL) { + if (!gave_emsg) { + semsg(_("E334: Menu not found: %s"), name_arg); + } + return NULL; + } + + return menu; +} + +/// Given a menu descriptor, e.g. "File.New", find it in the menu hierarchy and +/// execute it. void ex_emenu(exarg_T *eap) { char *arg = eap->arg; @@ -1590,39 +1686,8 @@ void ex_emenu(exarg_T *eap) arg = skipwhite(arg + 2); } - char *saved_name = xstrdup(arg); - vimmenu_T *menu = *get_root_menu(saved_name); - char *name = saved_name; - bool gave_emsg = false; - while (*name) { - // Find in the menu hierarchy - char *p = menu_name_skip(name); - - while (menu != NULL) { - if (menu_name_equal(name, menu)) { - if (*p == NUL && menu->children != NULL) { - emsg(_("E333: Menu path must lead to a menu item")); - gave_emsg = true; - menu = NULL; - } else if (*p != NUL && menu->children == NULL) { - emsg(_(e_notsubmenu)); - menu = NULL; - } - break; - } - menu = menu->next; - } - if (menu == NULL || *p == NUL) { - break; - } - menu = menu->children; - name = p; - } - xfree(saved_name); + vimmenu_T *menu = menu_getbyname(arg); if (menu == NULL) { - if (!gave_emsg) { - semsg(_("E334: Menu not found: %s"), arg); - } return; } @@ -1825,3 +1890,104 @@ static char *menu_translate_tab_and_shift(char *arg_start) return arg; } + +/// Get the information about a menu item in mode 'which' +static void menuitem_getinfo(const vimmenu_T *menu, int modes, dict_T *dict) + FUNC_ATTR_NONNULL_ALL +{ + tv_dict_add_str(dict, S_LEN("name"), menu->name); + tv_dict_add_str(dict, S_LEN("display"), menu->dname); + if (menu->actext != NULL) { + tv_dict_add_str(dict, S_LEN("accel"), menu->actext); + } + tv_dict_add_nr(dict, S_LEN("priority"), (int)menu->priority); + tv_dict_add_str(dict, S_LEN("modes"), get_menu_mode_str(menu->modes)); + + char buf[NUMBUFLEN]; + buf[utf_char2bytes(menu->mnemonic, buf)] = NUL; + tv_dict_add_str(dict, S_LEN("shortcut"), buf); + + if (menu->children == NULL) { // leaf menu + int bit; + + // Get the first mode in which the menu is available + for (bit = 0; (bit < MENU_MODES) && !((1 << bit) & modes); bit++) {} + + if (menu->strings[bit] != NULL) { + tv_dict_add_allocated_str(dict, S_LEN("rhs"), + *menu->strings[bit] == NUL + ? xstrdup("") + : str2special_save(menu->strings[bit], false, false)); + } + tv_dict_add_bool(dict, S_LEN("noremenu"), menu->noremap[bit] == REMAP_NONE); + tv_dict_add_bool(dict, S_LEN("script"), menu->noremap[bit] == REMAP_SCRIPT); + tv_dict_add_bool(dict, S_LEN("silent"), menu->silent[bit]); + tv_dict_add_bool(dict, S_LEN("enabled"), (menu->enabled & (1 << bit)) != 0); + } else { + // If there are submenus, add all the submenu display names + list_T *const l = tv_list_alloc(kListLenMayKnow); + tv_dict_add_list(dict, S_LEN("submenus"), l); + const vimmenu_T *child = menu->children; + while (child != NULL) { + tv_list_append_string(l, child->dname, -1); + child = child->next; + } + } +} + +/// "menu_info()" function +/// Return information about a menu (including all the child menus) +void f_menu_info(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + tv_dict_alloc_ret(rettv); + dict_T *const retdict = rettv->vval.v_dict; + + const char *const menu_name = tv_get_string_chk(&argvars[0]); + if (menu_name == NULL) { + return; + } + + // menu mode + const char *which; + if (argvars[1].v_type != VAR_UNKNOWN) { + which = tv_get_string_chk(&argvars[1]); + } else { + which = ""; // Default is modes for "menu" + } + if (which == NULL) { + return; + } + + const int modes = get_menu_cmd_modes(which, *which == '!', NULL, NULL); + + // Locate the specified menu or menu item + const vimmenu_T *menu = *get_root_menu(menu_name); + char *const saved_name = xstrdup(menu_name); + if (*saved_name != NUL) { + char *name = saved_name; + while (*name) { + // Find in the menu hierarchy + char *p = menu_name_skip(name); + while (menu != NULL) { + if (menu_name_equal(name, menu)) { + break; + } + menu = menu->next; + } + if (menu == NULL || *p == NUL) { + break; + } + menu = menu->children; + name = p; + } + } + xfree(saved_name); + + if (menu == NULL) { // specified menu not found + return; + } + + if (menu->modes & modes) { + menuitem_getinfo(menu, modes, retdict); + } +} diff --git a/src/nvim/testdir/test_menu.vim b/src/nvim/testdir/test_menu.vim index 4af75be514..24d7420751 100644 --- a/src/nvim/testdir/test_menu.vim +++ b/src/nvim/testdir/test_menu.vim @@ -89,6 +89,35 @@ func Test_menu_commands() unlet g:did_menu endfun +" Test various menu related errors +func Test_menu_errors() + menu Test.Foo :version + + " Error cases + call assert_fails('menu .Test.Foo :ls', 'E475:') + call assert_fails('menu Test. :ls', 'E330:') + call assert_fails('menu Foo. :ls', 'E331:') + call assert_fails('unmenu Test.Foo abc', 'E488:') + call assert_fails('menu :ls :ls', 'E792:') + call assert_fails('menu Test.:ls :ls', 'E792:') + call assert_fails('menu Test.Foo.Bar :ls', 'E327:') + call assert_fails('menu Test.-Sep-.Baz :ls', 'E332:') + call assert_fails('menu Foo.Bar.--.Baz :ls', 'E332:') + call assert_fails('menu disable Test.Foo.Bar', 'E327:') + call assert_fails('menu disable T.Foo', 'E329:') + call assert_fails('unmenu Test.Foo.Bar', 'E327:') + call assert_fails('cunmenu Test.Foo', 'E328:') + call assert_fails('unmenu Test.Bar', 'E329:') + call assert_fails('menu Test.Foo.Bar', 'E327:') + call assert_fails('cmenu Test.Foo', 'E328:') + call assert_fails('emenu x Test.Foo', 'E475:') + call assert_fails('emenu Test.Foo.Bar', 'E334:') + call assert_fails('menutranslate Test', 'E474:') + + silent! unmenu Foo + unmenu Test +endfunc + " Test for menu item completion in command line func Test_menu_expand() " Create the menu itmes for test @@ -119,8 +148,337 @@ func Test_menu_expand() \ "\\\"\", 'xt') call assert_equal('"emenu Buffers. Xmenu.', @:) + " Test for expanding only submenus + call feedkeys(":popup Xmenu.\\\"\", 'xt') + call assert_equal('"popup Xmenu.A1 A2 A3 A4', @:) + + " Test for expanding menus after enable/disable + call feedkeys(":menu enable Xmenu.\\\"\", 'xt') + call assert_equal('"menu enable Xmenu.A1. A2. A3. A4.', @:) + call feedkeys(":menu disable Xmenu.\\\"\", 'xt') + call assert_equal('"menu disable Xmenu.A1. A2. A3. A4.', @:) + + " Test for expanding non-existing menu path + call feedkeys(":menu xyz.\\\"\", 'xt') + call assert_equal('"menu xyz.', @:) + call feedkeys(":menu Xmenu.A1.A1B1.xyz.\\\"\", 'xt') + call assert_equal('"menu Xmenu.A1.A1B1.xyz.', @:) + set wildmenu& unmenu Xmenu + + " Test for expanding popup menus with some hidden items + menu Xmenu.foo.A1 a1 + menu Xmenu.]bar bar + menu Xmenu.]baz.B1 b1 + menu Xmenu.-sep- : + call feedkeys(":popup Xmenu.\\\"\", 'xt') + call assert_equal('"popup Xmenu.foo', @:) + unmenu Xmenu + +endfunc + +" Test for the menu_info() function +func Test_menu_info() + " Define menus with various attributes + 10nnoremenu 10.10 T&est.F&oo :echo 'foo' + 10nmenu 10.20 T&est.B&ar:bar :echo 'bar' + 10nmenu