aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/ex_cmds2.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/ex_cmds2.c')
-rw-r--r--src/nvim/ex_cmds2.c951
1 files changed, 86 insertions, 865 deletions
diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c
index e06a62e0f6..6542ab41f5 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -20,14 +20,12 @@
#include "nvim/buffer.h"
#include "nvim/change.h"
#include "nvim/charset.h"
+#include "nvim/debugger.h"
#include "nvim/eval/userfunc.h"
#include "nvim/ex_cmds.h"
-#include "nvim/ex_docmd.h"
#include "nvim/ex_eval.h"
#include "nvim/ex_getln.h"
#include "nvim/fileio.h"
-#include "nvim/getchar.h"
-#include "nvim/globals.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
@@ -43,13 +41,11 @@
#include "nvim/path.h"
#include "nvim/quickfix.h"
#include "nvim/regexp.h"
-#include "nvim/screen.h"
#include "nvim/strings.h"
#include "nvim/undo.h"
#include "nvim/version.h"
#include "nvim/window.h"
#include "nvim/profile.h"
-#include "nvim/os/os.h"
#include "nvim/os/shell.h"
#include "nvim/os/fs_defs.h"
#include "nvim/api/private/helpers.h"
@@ -119,808 +115,6 @@ struct source_cookie {
# include "ex_cmds2.c.generated.h"
#endif
-/// batch mode debugging: don't save and restore typeahead.
-static bool debug_greedy = false;
-
-static char *debug_oldval = NULL; // old and newval for debug expressions
-static char *debug_newval = NULL;
-
-/// Debug mode. Repeatedly get Ex commands, until told to continue normal
-/// execution.
-void do_debug(char_u *cmd)
-{
- int save_msg_scroll = msg_scroll;
- int save_State = State;
- int save_did_emsg = did_emsg;
- const bool save_cmd_silent = cmd_silent;
- int save_msg_silent = msg_silent;
- int save_emsg_silent = emsg_silent;
- bool save_redir_off = redir_off;
- tasave_T typeaheadbuf;
- bool typeahead_saved = false;
- int save_ignore_script = 0;
- int save_ex_normal_busy;
- int n;
- char_u *cmdline = NULL;
- char_u *p;
- char *tail = NULL;
- static int last_cmd = 0;
-#define CMD_CONT 1
-#define CMD_NEXT 2
-#define CMD_STEP 3
-#define CMD_FINISH 4
-#define CMD_QUIT 5
-#define CMD_INTERRUPT 6
-#define CMD_BACKTRACE 7
-#define CMD_FRAME 8
-#define CMD_UP 9
-#define CMD_DOWN 10
-
-
- RedrawingDisabled++; // don't redisplay the window
- no_wait_return++; // don't wait for return
- did_emsg = false; // don't use error from debugged stuff
- cmd_silent = false; // display commands
- msg_silent = false; // display messages
- emsg_silent = false; // display error messages
- redir_off = true; // don't redirect debug commands
-
- State = NORMAL;
- debug_mode = true;
-
- if (!debug_did_msg) {
- MSG(_("Entering Debug mode. Type \"cont\" to continue."));
- }
- if (debug_oldval != NULL) {
- smsg(_("Oldval = \"%s\""), debug_oldval);
- xfree(debug_oldval);
- debug_oldval = NULL;
- }
- if (debug_newval != NULL) {
- smsg(_("Newval = \"%s\""), debug_newval);
- xfree(debug_newval);
- debug_newval = NULL;
- }
- if (sourcing_name != NULL) {
- msg(sourcing_name);
- }
- if (sourcing_lnum != 0) {
- smsg(_("line %" PRId64 ": %s"), (int64_t)sourcing_lnum, cmd);
- } else {
- smsg(_("cmd: %s"), cmd);
- }
- // Repeat getting a command and executing it.
- for (;; ) {
- msg_scroll = true;
- need_wait_return = false;
- // Save the current typeahead buffer and replace it with an empty one.
- // This makes sure we get input from the user here and don't interfere
- // with the commands being executed. Reset "ex_normal_busy" to avoid
- // the side effects of using ":normal". Save the stuff buffer and make
- // it empty. Set ignore_script to avoid reading from script input.
- save_ex_normal_busy = ex_normal_busy;
- ex_normal_busy = 0;
- if (!debug_greedy) {
- save_typeahead(&typeaheadbuf);
- typeahead_saved = true;
- save_ignore_script = ignore_script;
- ignore_script = true;
- }
-
- xfree(cmdline);
- cmdline = (char_u *)getcmdline_prompt('>', NULL, 0, EXPAND_NOTHING, NULL,
- CALLBACK_NONE);
-
- if (typeahead_saved) {
- restore_typeahead(&typeaheadbuf);
- ignore_script = save_ignore_script;
- }
- ex_normal_busy = save_ex_normal_busy;
-
- cmdline_row = msg_row;
- msg_starthere();
- if (cmdline != NULL) {
- // If this is a debug command, set "last_cmd".
- // If not, reset "last_cmd".
- // For a blank line use previous command.
- p = skipwhite(cmdline);
- if (*p != NUL) {
- switch (*p) {
- case 'c': last_cmd = CMD_CONT;
- tail = "ont";
- break;
- case 'n': last_cmd = CMD_NEXT;
- tail = "ext";
- break;
- case 's': last_cmd = CMD_STEP;
- tail = "tep";
- break;
- case 'f':
- last_cmd = 0;
- if (p[1] == 'r') {
- last_cmd = CMD_FRAME;
- tail = "rame";
- } else {
- last_cmd = CMD_FINISH;
- tail = "inish";
- }
- break;
- case 'q': last_cmd = CMD_QUIT;
- tail = "uit";
- break;
- case 'i': last_cmd = CMD_INTERRUPT;
- tail = "nterrupt";
- break;
- case 'b':
- last_cmd = CMD_BACKTRACE;
- if (p[1] == 't') {
- tail = "t";
- } else {
- tail = "acktrace";
- }
- break;
- case 'w':
- last_cmd = CMD_BACKTRACE;
- tail = "here";
- break;
- case 'u':
- last_cmd = CMD_UP;
- tail = "p";
- break;
- case 'd':
- last_cmd = CMD_DOWN;
- tail = "own";
- break;
- default: last_cmd = 0;
- }
- if (last_cmd != 0) {
- // Check that the tail matches.
- p++;
- while (*p != NUL && *p == *tail) {
- p++;
- tail++;
- }
- if (ASCII_ISALPHA(*p) && last_cmd != CMD_FRAME) {
- last_cmd = 0;
- }
- }
- }
-
- if (last_cmd != 0) {
- // Execute debug command: decided where to break next and return.
- switch (last_cmd) {
- case CMD_CONT:
- debug_break_level = -1;
- break;
- case CMD_NEXT:
- debug_break_level = ex_nesting_level;
- break;
- case CMD_STEP:
- debug_break_level = 9999;
- break;
- case CMD_FINISH:
- debug_break_level = ex_nesting_level - 1;
- break;
- case CMD_QUIT:
- got_int = true;
- debug_break_level = -1;
- break;
- case CMD_INTERRUPT:
- got_int = true;
- debug_break_level = 9999;
- // Do not repeat ">interrupt" cmd, continue stepping.
- last_cmd = CMD_STEP;
- break;
- case CMD_BACKTRACE:
- do_showbacktrace(cmd);
- continue;
- case CMD_FRAME:
- if (*p == NUL) {
- do_showbacktrace(cmd);
- } else {
- p = skipwhite(p);
- do_setdebugtracelevel(p);
- }
- continue;
- case CMD_UP:
- debug_backtrace_level++;
- do_checkbacktracelevel();
- continue;
- case CMD_DOWN:
- debug_backtrace_level--;
- do_checkbacktracelevel();
- continue;
- }
- // Going out reset backtrace_level
- debug_backtrace_level = 0;
- break;
- }
-
- // don't debug this command
- n = debug_break_level;
- debug_break_level = -1;
- (void)do_cmdline(cmdline, getexline, NULL,
- DOCMD_VERBOSE|DOCMD_EXCRESET);
- debug_break_level = n;
- }
- lines_left = (int)(Rows - 1);
- }
- xfree(cmdline);
-
- RedrawingDisabled--;
- no_wait_return--;
- redraw_all_later(NOT_VALID);
- need_wait_return = false;
- msg_scroll = save_msg_scroll;
- lines_left = (int)(Rows - 1);
- State = save_State;
- debug_mode = false;
- did_emsg = save_did_emsg;
- cmd_silent = save_cmd_silent;
- msg_silent = save_msg_silent;
- emsg_silent = save_emsg_silent;
- redir_off = save_redir_off;
-
- // Only print the message again when typing a command before coming back here.
- debug_did_msg = true;
-}
-
-static int get_maxbacktrace_level(void)
-{
- int maxbacktrace = 0;
-
- if (sourcing_name != NULL) {
- char *p = (char *)sourcing_name;
- char *q;
- while ((q = strstr(p, "..")) != NULL) {
- p = q + 2;
- maxbacktrace++;
- }
- }
- return maxbacktrace;
-}
-
-static void do_setdebugtracelevel(char_u *arg)
-{
- int level = atoi((char *)arg);
- if (*arg == '+' || level < 0) {
- debug_backtrace_level += level;
- } else {
- debug_backtrace_level = level;
- }
-
- do_checkbacktracelevel();
-}
-
-static void do_checkbacktracelevel(void)
-{
- if (debug_backtrace_level < 0) {
- debug_backtrace_level = 0;
- MSG(_("frame is zero"));
- } else {
- int max = get_maxbacktrace_level();
- if (debug_backtrace_level > max) {
- debug_backtrace_level = max;
- smsg(_("frame at highest level: %d"), max);
- }
- }
-}
-
-static void do_showbacktrace(char_u *cmd)
-{
- if (sourcing_name != NULL) {
- int i = 0;
- int max = get_maxbacktrace_level();
- char *cur = (char *)sourcing_name;
- while (!got_int) {
- char *next = strstr(cur, "..");
- if (next != NULL) {
- *next = NUL;
- }
- if (i == max - debug_backtrace_level) {
- smsg("->%d %s", max - i, cur);
- } else {
- smsg(" %d %s", max - i, cur);
- }
- i++;
- if (next == NULL) {
- break;
- }
- *next = '.';
- cur = next + 2;
- }
- }
- if (sourcing_lnum != 0) {
- smsg(_("line %" PRId64 ": %s"), (int64_t)sourcing_lnum, cmd);
- } else {
- smsg(_("cmd: %s"), cmd);
- }
-}
-
-
-/// ":debug".
-void ex_debug(exarg_T *eap)
-{
- int debug_break_level_save = debug_break_level;
-
- debug_break_level = 9999;
- do_cmdline_cmd((char *)eap->arg);
- debug_break_level = debug_break_level_save;
-}
-
-static char_u *debug_breakpoint_name = NULL;
-static linenr_T debug_breakpoint_lnum;
-
-/// When debugging or a breakpoint is set on a skipped command, no debug prompt
-/// is shown by do_one_cmd(). This situation is indicated by debug_skipped, and
-/// debug_skipped_name is then set to the source name in the breakpoint case. If
-/// a skipped command decides itself that a debug prompt should be displayed, it
-/// can do so by calling dbg_check_skipped().
-static int debug_skipped;
-static char_u *debug_skipped_name;
-
-/// Go to debug mode when a breakpoint was encountered or "ex_nesting_level" is
-/// at or below the break level. But only when the line is actually
-/// executed. Return true and set breakpoint_name for skipped commands that
-/// decide to execute something themselves.
-/// Called from do_one_cmd() before executing a command.
-void dbg_check_breakpoint(exarg_T *eap)
-{
- char_u *p;
-
- debug_skipped = false;
- if (debug_breakpoint_name != NULL) {
- if (!eap->skip) {
- // replace K_SNR with "<SNR>"
- if (debug_breakpoint_name[0] == K_SPECIAL
- && debug_breakpoint_name[1] == KS_EXTRA
- && debug_breakpoint_name[2] == (int)KE_SNR) {
- p = (char_u *)"<SNR>";
- } else {
- p = (char_u *)"";
- }
- smsg(_("Breakpoint in \"%s%s\" line %" PRId64),
- p,
- debug_breakpoint_name + (*p == NUL ? 0 : 3),
- (int64_t)debug_breakpoint_lnum);
- debug_breakpoint_name = NULL;
- do_debug(eap->cmd);
- } else {
- debug_skipped = true;
- debug_skipped_name = debug_breakpoint_name;
- debug_breakpoint_name = NULL;
- }
- } else if (ex_nesting_level <= debug_break_level) {
- if (!eap->skip) {
- do_debug(eap->cmd);
- } else {
- debug_skipped = true;
- debug_skipped_name = NULL;
- }
- }
-}
-
-/// Go to debug mode if skipped by dbg_check_breakpoint() because eap->skip was
-/// set.
-///
-/// @return true when the debug mode is entered this time.
-bool dbg_check_skipped(exarg_T *eap)
-{
- int prev_got_int;
-
- if (debug_skipped) {
- // Save the value of got_int and reset it. We don't want a previous
- // interruption cause flushing the input buffer.
- prev_got_int = got_int;
- got_int = false;
- debug_breakpoint_name = debug_skipped_name;
- // eap->skip is true
- eap->skip = false;
- dbg_check_breakpoint(eap);
- eap->skip = true;
- got_int |= prev_got_int;
- return true;
- }
- return false;
-}
-
-/// The list of breakpoints: dbg_breakp.
-/// This is a grow-array of structs.
-struct debuggy {
- int dbg_nr; ///< breakpoint number
- int dbg_type; ///< DBG_FUNC or DBG_FILE or DBG_EXPR
- char_u *dbg_name; ///< function, expression or file name
- regprog_T *dbg_prog; ///< regexp program
- linenr_T dbg_lnum; ///< line number in function or file
- int dbg_forceit; ///< ! used
- typval_T *dbg_val; ///< last result of watchexpression
- int dbg_level; ///< stored nested level for expr
-};
-
-static garray_T dbg_breakp = { 0, 0, sizeof(struct debuggy), 4, NULL };
-#define BREAKP(idx) (((struct debuggy *)dbg_breakp.ga_data)[idx])
-#define DEBUGGY(gap, idx) (((struct debuggy *)gap->ga_data)[idx])
-static int last_breakp = 0; // nr of last defined breakpoint
-
-// Profiling uses file and func names similar to breakpoints.
-static garray_T prof_ga = { 0, 0, sizeof(struct debuggy), 4, NULL };
-#define DBG_FUNC 1
-#define DBG_FILE 2
-#define DBG_EXPR 3
-
-
-/// Parse the arguments of ":profile", ":breakadd" or ":breakdel" and put them
-/// in the entry just after the last one in dbg_breakp. Note that "dbg_name"
-/// is allocated.
-/// Returns FAIL for failure.
-///
-/// @param arg
-/// @param gap either &dbg_breakp or &prof_ga
-static int dbg_parsearg(char_u *arg, garray_T *gap)
-{
- char_u *p = arg;
- char_u *q;
- struct debuggy *bp;
- bool here = false;
-
- ga_grow(gap, 1);
-
- bp = &DEBUGGY(gap, gap->ga_len);
-
- // Find "func" or "file".
- if (STRNCMP(p, "func", 4) == 0) {
- bp->dbg_type = DBG_FUNC;
- } else if (STRNCMP(p, "file", 4) == 0) {
- bp->dbg_type = DBG_FILE;
- } else if (gap != &prof_ga && STRNCMP(p, "here", 4) == 0) {
- if (curbuf->b_ffname == NULL) {
- EMSG(_(e_noname));
- return FAIL;
- }
- bp->dbg_type = DBG_FILE;
- here = true;
- } else if (gap != &prof_ga && STRNCMP(p, "expr", 4) == 0) {
- bp->dbg_type = DBG_EXPR;
- } else {
- EMSG2(_(e_invarg2), p);
- return FAIL;
- }
- p = skipwhite(p + 4);
-
- // Find optional line number.
- if (here) {
- bp->dbg_lnum = curwin->w_cursor.lnum;
- } else if (gap != &prof_ga && ascii_isdigit(*p)) {
- bp->dbg_lnum = getdigits_long(&p, true, 0);
- p = skipwhite(p);
- } else {
- bp->dbg_lnum = 0;
- }
-
- // Find the function or file name. Don't accept a function name with ().
- if ((!here && *p == NUL)
- || (here && *p != NUL)
- || (bp->dbg_type == DBG_FUNC && strstr((char *)p, "()") != NULL)) {
- EMSG2(_(e_invarg2), arg);
- return FAIL;
- }
-
- if (bp->dbg_type == DBG_FUNC) {
- bp->dbg_name = vim_strsave(p);
- } else if (here) {
- bp->dbg_name = vim_strsave(curbuf->b_ffname);
- } else if (bp->dbg_type == DBG_EXPR) {
- bp->dbg_name = vim_strsave(p);
- bp->dbg_val = eval_expr(bp->dbg_name);
- } else {
- // Expand the file name in the same way as do_source(). This means
- // doing it twice, so that $DIR/file gets expanded when $DIR is
- // "~/dir".
- q = expand_env_save(p);
- if (q == NULL) {
- return FAIL;
- }
- p = expand_env_save(q);
- xfree(q);
- if (p == NULL) {
- return FAIL;
- }
- if (*p != '*') {
- bp->dbg_name = (char_u *)fix_fname((char *)p);
- xfree(p);
- } else {
- bp->dbg_name = p;
- }
- }
-
- if (bp->dbg_name == NULL) {
- return FAIL;
- }
- return OK;
-}
-
-/// ":breakadd". Also used for ":profile".
-void ex_breakadd(exarg_T *eap)
-{
- struct debuggy *bp;
- garray_T *gap;
-
- gap = &dbg_breakp;
- if (eap->cmdidx == CMD_profile) {
- gap = &prof_ga;
- }
-
- if (dbg_parsearg(eap->arg, gap) == OK) {
- bp = &DEBUGGY(gap, gap->ga_len);
- bp->dbg_forceit = eap->forceit;
-
- if (bp->dbg_type != DBG_EXPR) {
- char_u *pat = file_pat_to_reg_pat(bp->dbg_name, NULL, NULL, false);
- if (pat != NULL) {
- bp->dbg_prog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
- xfree(pat);
- }
- if (pat == NULL || bp->dbg_prog == NULL) {
- xfree(bp->dbg_name);
- } else {
- if (bp->dbg_lnum == 0) { // default line number is 1
- bp->dbg_lnum = 1;
- }
- if (eap->cmdidx != CMD_profile) {
- DEBUGGY(gap, gap->ga_len).dbg_nr = ++last_breakp;
- debug_tick++;
- }
- gap->ga_len++;
- }
- } else {
- // DBG_EXPR
- DEBUGGY(gap, gap->ga_len++).dbg_nr = ++last_breakp;
- debug_tick++;
- }
- }
-}
-
-/// ":debuggreedy".
-void ex_debuggreedy(exarg_T *eap)
-{
- if (eap->addr_count == 0 || eap->line2 != 0) {
- debug_greedy = true;
- } else {
- debug_greedy = false;
- }
-}
-
-/// ":breakdel" and ":profdel".
-void ex_breakdel(exarg_T *eap)
-{
- struct debuggy *bp, *bpi;
- int nr;
- int todel = -1;
- bool del_all = false;
- linenr_T best_lnum = 0;
- garray_T *gap;
-
- gap = &dbg_breakp;
- if (eap->cmdidx == CMD_profdel) {
- gap = &prof_ga;
- }
-
- if (ascii_isdigit(*eap->arg)) {
- // ":breakdel {nr}"
- nr = atoi((char *)eap->arg);
- for (int i = 0; i < gap->ga_len; i++) {
- if (DEBUGGY(gap, i).dbg_nr == nr) {
- todel = i;
- break;
- }
- }
- } else if (*eap->arg == '*') {
- todel = 0;
- del_all = true;
- } else {
- // ":breakdel {func|file|expr} [lnum] {name}"
- if (dbg_parsearg(eap->arg, gap) == FAIL) {
- return;
- }
- bp = &DEBUGGY(gap, gap->ga_len);
- for (int i = 0; i < gap->ga_len; i++) {
- bpi = &DEBUGGY(gap, i);
- if (bp->dbg_type == bpi->dbg_type
- && STRCMP(bp->dbg_name, bpi->dbg_name) == 0
- && (bp->dbg_lnum == bpi->dbg_lnum
- || (bp->dbg_lnum == 0
- && (best_lnum == 0
- || bpi->dbg_lnum < best_lnum)))) {
- todel = i;
- best_lnum = bpi->dbg_lnum;
- }
- }
- xfree(bp->dbg_name);
- }
-
- if (todel < 0) {
- EMSG2(_("E161: Breakpoint not found: %s"), eap->arg);
- } else {
- while (!GA_EMPTY(gap)) {
- xfree(DEBUGGY(gap, todel).dbg_name);
- if (DEBUGGY(gap, todel).dbg_type == DBG_EXPR
- && DEBUGGY(gap, todel).dbg_val != NULL) {
- tv_free(DEBUGGY(gap, todel).dbg_val);
- }
- vim_regfree(DEBUGGY(gap, todel).dbg_prog);
- gap->ga_len--;
- if (todel < gap->ga_len) {
- memmove(&DEBUGGY(gap, todel), &DEBUGGY(gap, todel + 1),
- (size_t)(gap->ga_len - todel) * sizeof(struct debuggy));
- }
- if (eap->cmdidx == CMD_breakdel) {
- debug_tick++;
- }
- if (!del_all) {
- break;
- }
- }
-
- // If all breakpoints were removed clear the array.
- if (GA_EMPTY(gap)) {
- ga_clear(gap);
- }
- }
-}
-
-/// ":breaklist".
-void ex_breaklist(exarg_T *eap)
-{
- struct debuggy *bp;
-
- if (GA_EMPTY(&dbg_breakp)) {
- MSG(_("No breakpoints defined"));
- } else {
- for (int i = 0; i < dbg_breakp.ga_len; i++) {
- bp = &BREAKP(i);
- if (bp->dbg_type == DBG_FILE) {
- home_replace(NULL, bp->dbg_name, NameBuff, MAXPATHL, true);
- }
- if (bp->dbg_type != DBG_EXPR) {
- smsg(_("%3d %s %s line %" PRId64),
- bp->dbg_nr,
- bp->dbg_type == DBG_FUNC ? "func" : "file",
- bp->dbg_type == DBG_FUNC ? bp->dbg_name : NameBuff,
- (int64_t)bp->dbg_lnum);
- } else {
- smsg(_("%3d expr %s"), bp->dbg_nr, bp->dbg_name);
- }
- }
- }
-}
-
-/// Find a breakpoint for a function or sourced file.
-/// Returns line number at which to break; zero when no matching breakpoint.
-linenr_T
-dbg_find_breakpoint(
- bool file, // true for a file, false for a function
- char_u *fname, // file or function name
- linenr_T after // after this line number
-)
-{
- return debuggy_find(file, fname, after, &dbg_breakp, NULL);
-}
-
-/// @param file true for a file, false for a function
-/// @param fname file or function name
-/// @param fp[out] forceit
-///
-/// @returns true if profiling is on for a function or sourced file.
-bool has_profiling(bool file, char_u *fname, bool *fp)
-{
- return debuggy_find(file, fname, (linenr_T)0, &prof_ga, fp)
- != (linenr_T)0;
-}
-
-/// Common code for dbg_find_breakpoint() and has_profiling().
-static linenr_T
-debuggy_find(
- bool file, // true for a file, false for a function
- char_u *fname, // file or function name
- linenr_T after, // after this line number
- garray_T *gap, // either &dbg_breakp or &prof_ga
- bool *fp // if not NULL: return forceit
-)
-{
- struct debuggy *bp;
- linenr_T lnum = 0;
- char_u *name = fname;
- int prev_got_int;
-
- // Return quickly when there are no breakpoints.
- if (GA_EMPTY(gap)) {
- return (linenr_T)0;
- }
-
- // Replace K_SNR in function name with "<SNR>".
- if (!file && fname[0] == K_SPECIAL) {
- name = xmalloc(STRLEN(fname) + 3);
- STRCPY(name, "<SNR>");
- STRCPY(name + 5, fname + 3);
- }
-
- for (int i = 0; i < gap->ga_len; i++) {
- // Skip entries that are not useful or are for a line that is beyond
- // an already found breakpoint.
- bp = &DEBUGGY(gap, i);
- if ((bp->dbg_type == DBG_FILE) == file
- && bp->dbg_type != DBG_EXPR
- && (gap == &prof_ga
- || (bp->dbg_lnum > after && (lnum == 0 || bp->dbg_lnum < lnum)))) {
- // Save the value of got_int and reset it. We don't want a
- // previous interruption cancel matching, only hitting CTRL-C
- // while matching should abort it.
- prev_got_int = got_int;
- got_int = false;
- if (vim_regexec_prog(&bp->dbg_prog, false, name, (colnr_T)0)) {
- lnum = bp->dbg_lnum;
- if (fp != NULL) {
- *fp = bp->dbg_forceit;
- }
- }
- got_int |= prev_got_int;
- } else if (bp->dbg_type == DBG_EXPR) {
- bool line = false;
-
- prev_got_int = got_int;
- got_int = false;
-
- typval_T *tv = eval_expr(bp->dbg_name);
- if (tv != NULL) {
- if (bp->dbg_val == NULL) {
- debug_oldval = typval_tostring(NULL);
- bp->dbg_val = tv;
- debug_newval = typval_tostring(bp->dbg_val);
- line = true;
- } else {
- if (typval_compare(tv, bp->dbg_val, EXPR_IS, false) == OK
- && tv->vval.v_number == false) {
- line = true;
- debug_oldval = typval_tostring(bp->dbg_val);
- // Need to evaluate again, typval_compare() overwrites "tv".
- typval_T *v = eval_expr(bp->dbg_name);
- debug_newval = typval_tostring(v);
- tv_free(bp->dbg_val);
- bp->dbg_val = v;
- }
- tv_free(tv);
- }
- } else if (bp->dbg_val != NULL) {
- debug_oldval = typval_tostring(bp->dbg_val);
- debug_newval = typval_tostring(NULL);
- tv_free(bp->dbg_val);
- bp->dbg_val = NULL;
- line = true;
- }
-
- if (line) {
- lnum = after > 0 ? after : 1;
- break;
- }
-
- got_int |= prev_got_int;
- }
- }
- if (name != fname) {
- xfree(name);
- }
-
- return lnum;
-}
-
-/// Called when a breakpoint was encountered.
-void dbg_breakpoint(char_u *name, linenr_T lnum)
-{
- // We need to check if this line is actually executed in do_one_cmd()
- debug_breakpoint_name = name;
- debug_breakpoint_lnum = lnum;
-}
-
static char_u *profile_fname = NULL;
/// ":profile cmd args"
@@ -2635,44 +1829,42 @@ static void cmd_source(char_u *fname, exarg_T *eap)
}
}
-typedef struct {
- linenr_T curr_lnum;
- const linenr_T final_lnum;
-} GetBufferLineCookie;
-
-/// Get one line from the current selection in the buffer.
-/// Called by do_cmdline() when it's called from cmd_source_buffer().
+/// Concatenate VimL line if it starts with a line continuation into a growarray
+/// (excluding the continuation chars and leading whitespace)
///
-/// @return pointer to allocated line, or NULL for end-of-file or
-/// some error.
-static char_u *get_buffer_line(int c, void *cookie, int indent, bool do_concat)
-{
- GetBufferLineCookie *p = cookie;
- if (p->curr_lnum > p->final_lnum) {
- return NULL;
- }
- char_u *curr_line = ml_get(p->curr_lnum);
- p->curr_lnum++;
- return (char_u *)xstrdup((const char *)curr_line);
-}
-
-static void cmd_source_buffer(const exarg_T *eap)
+/// @note Growsize of the growarray may be changed to speed up concatenations!
+///
+/// @param ga the growarray to append to
+/// @param init_growsize the starting growsize value of the growarray
+/// @param p pointer to the beginning of the line to consider
+/// @param len the length of this line
+///
+/// @return true if this line did begin with a continuation (the next line
+/// should also be considered, if it exists); false otherwise
+static bool concat_continued_line(garray_T *const ga, const int init_growsize,
+ const char_u *const p, size_t len)
FUNC_ATTR_NONNULL_ALL
{
- GetBufferLineCookie cookie = {
- .curr_lnum = eap->line1,
- .final_lnum = eap->line2,
- };
- if (curbuf != NULL && curbuf->b_fname
- && path_with_extension((const char *)curbuf->b_fname, "lua")) {
- nlua_source_using_linegetter(get_buffer_line, (void *)&cookie,
- ":source (no file)");
- } else {
- source_using_linegetter((void *)&cookie, get_buffer_line,
- ":source (no file)");
+ const char_u *const line = skipwhite_len(p, len);
+ len -= (size_t)(line - p);
+ // Skip lines starting with '\" ', concat lines starting with '\'
+ if (len >= 3 && STRNCMP(line, "\"\\ ", 3) == 0) {
+ return true;
+ } else if (len == 0 || line[0] != '\\') {
+ return false;
}
+ if (ga->ga_len > init_growsize) {
+ ga_set_growsize(ga, MAX(ga->ga_len, 8000));
+ }
+ ga_concat_len(ga, (const char *)line + 1, len - 1);
+ return true;
}
+typedef struct {
+ linenr_T curr_lnum;
+ const linenr_T final_lnum;
+} GetBufferLineCookie;
+
/// ":source" and associated commands.
///
/// @return address holding the next breakpoint line for a source cookie
@@ -2725,17 +1917,27 @@ typedef struct {
static char_u *get_str_line(int c, void *cookie, int indent, bool do_concat)
{
GetStrLineCookie *p = cookie;
- size_t i = p->offset;
- if (strlen((char *)p->buf) <= p->offset) {
+ if (STRLEN(p->buf) <= p->offset) {
return NULL;
}
- while (!(p->buf[i] == '\n' || p->buf[i] == '\0')) {
- i++;
+ const char_u *line = p->buf + p->offset;
+ const char_u *eol = skip_to_newline(line);
+ garray_T ga;
+ ga_init(&ga, sizeof(char_u), 400);
+ ga_concat_len(&ga, (const char *)line, (size_t)(eol - line));
+ if (do_concat && vim_strchr(p_cpo, CPO_CONCAT) == NULL) {
+ while (eol[0] != NUL) {
+ line = eol + 1;
+ const char_u *const next_eol = skip_to_newline(line);
+ if (!concat_continued_line(&ga, 400, line, (size_t)(next_eol - line))) {
+ break;
+ }
+ eol = next_eol;
+ }
}
- size_t line_length = i - p->offset;
- char_u *buf = xmemdupz(p->buf + p->offset, line_length);
- p->offset = i + 1;
- return buf;
+ ga_append(&ga, NUL);
+ p->offset = (size_t)(eol - p->buf) + 1;
+ return ga.ga_data;
}
static int source_using_linegetter(void *cookie,
@@ -2770,6 +1972,40 @@ static int source_using_linegetter(void *cookie,
return retval;
}
+static void cmd_source_buffer(const exarg_T *const eap)
+ FUNC_ATTR_NONNULL_ALL
+{
+ if (curbuf == NULL) {
+ return;
+ }
+ garray_T ga;
+ ga_init(&ga, sizeof(char_u), 400);
+ const linenr_T final_lnum = eap->line2;
+ // Copy the contents to be executed.
+ for (linenr_T curr_lnum = eap->line1; curr_lnum <= final_lnum; curr_lnum++) {
+ // Adjust growsize to current length to speed up concatenating many lines.
+ if (ga.ga_len > 400) {
+ ga_set_growsize(&ga, MAX(ga.ga_len, 8000));
+ }
+ ga_concat(&ga, ml_get(curr_lnum));
+ ga_append(&ga, NL);
+ }
+ ((char_u *)ga.ga_data)[ga.ga_len - 1] = NUL;
+ const GetStrLineCookie cookie = {
+ .buf = ga.ga_data,
+ .offset = 0,
+ };
+ if (curbuf->b_fname
+ && path_with_extension((const char *)curbuf->b_fname, "lua")) {
+ nlua_source_using_linegetter(get_str_line, (void *)&cookie,
+ ":source (no file)");
+ } else {
+ source_using_linegetter((void *)&cookie, get_str_line,
+ ":source (no file)");
+ }
+ ga_clear(&ga);
+}
+
/// Executes lines in `src` as Ex commands.
///
/// @see do_source()
@@ -3227,26 +2463,11 @@ char_u *getsourceline(int c, void *cookie, int indent, bool do_concat)
ga_init(&ga, (int)sizeof(char_u), 400);
ga_concat(&ga, line);
- if (*p == '\\') {
- ga_concat(&ga, p + 1);
- }
- for (;; ) {
+ while (sp->nextline != NULL
+ && concat_continued_line(&ga, 400, sp->nextline,
+ STRLEN(sp->nextline))) {
xfree(sp->nextline);
sp->nextline = get_one_sourceline(sp);
- if (sp->nextline == NULL) {
- break;
- }
- p = skipwhite(sp->nextline);
- if (*p == '\\') {
- // Adjust the growsize to the current length to speed up
- // concatenating many lines.
- if (ga.ga_len > 400) {
- ga_set_growsize(&ga, (ga.ga_len > 8000) ? 8000 : ga.ga_len);
- }
- ga_concat(&ga, p + 1);
- } else if (p[0] != '"' || p[1] != '\\' || p[2] != ' ') {
- break;
- }
}
ga_append(&ga, NUL);
xfree(line);