diff options
author | zeertzjq <zeertzjq@outlook.com> | 2022-12-03 21:44:13 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-12-03 21:44:13 +0800 |
commit | e642825e281b7a9f259b8bc0b83b94a78b18a2c4 (patch) | |
tree | 846f82101d13a45c8c5faf26971472ea880af4ea /src | |
parent | c768b578faba671beab435954dc4e5a321c94728 (diff) | |
parent | 0cb90114d4c4801457e286c9b72ad0f394877d05 (diff) | |
download | rneovim-e642825e281b7a9f259b8bc0b83b94a78b18a2c4.tar.gz rneovim-e642825e281b7a9f259b8bc0b83b94a78b18a2c4.tar.bz2 rneovim-e642825e281b7a9f259b8bc0b83b94a78b18a2c4.zip |
Merge pull request #21274 from zeertzjq/vim-8.2.3992
vim-patch:8.2.{3992,4261,4262},9.0.{0110,0577}
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/ex_eval.c | 471 | ||||
-rw-r--r-- | src/nvim/help.c | 43 | ||||
-rw-r--r-- | src/nvim/regexp.c | 4 | ||||
-rw-r--r-- | src/nvim/testdir/runtest.vim | 2 | ||||
-rw-r--r-- | src/nvim/testdir/test_functions.vim | 5 | ||||
-rw-r--r-- | src/nvim/testdir/test_help.vim | 73 | ||||
-rw-r--r-- | src/nvim/testdir/test_help_tagjump.vim | 12 | ||||
-rw-r--r-- | src/nvim/testdir/test_ins_complete.vim | 6 | ||||
-rw-r--r-- | src/nvim/testdir/test_normal.vim | 2 | ||||
-rw-r--r-- | src/nvim/testdir/test_tagfunc.vim | 2 | ||||
-rw-r--r-- | src/nvim/testdir/test_trycatch.vim | 22 | ||||
-rw-r--r-- | src/nvim/testdir/vim9.vim | 32 |
12 files changed, 395 insertions, 279 deletions
diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c index f696ab3900..ed83725740 100644 --- a/src/nvim/ex_eval.c +++ b/src/nvim/ex_eval.c @@ -1420,108 +1420,107 @@ void ex_finally(exarg_T *eap) int pending = CSTP_NONE; cstack_T *const cstack = eap->cstack; - if (cstack->cs_trylevel <= 0 || cstack->cs_idx < 0) { - eap->errmsg = _("E606: :finally without :try"); - } else { - if (!(cstack->cs_flags[cstack->cs_idx] & CSF_TRY)) { - eap->errmsg = get_end_emsg(cstack); - for (idx = cstack->cs_idx - 1; idx > 0; idx--) { - if (cstack->cs_flags[idx] & CSF_TRY) { - break; - } - } - // Make this error pending, so that the commands in the following - // finally clause can be executed. This overrules also a pending - // ":continue", ":break", ":return", or ":finish". - pending = CSTP_ERROR; - } else { - idx = cstack->cs_idx; + for (idx = cstack->cs_idx; idx >= 0; idx--) { + if (cstack->cs_flags[idx] & CSF_TRY) { + break; } + } + if (cstack->cs_trylevel <= 0 || idx < 0) { + eap->errmsg = _("E606: :finally without :try"); + return; + } - if (cstack->cs_flags[idx] & CSF_FINALLY) { - // Give up for a multiple ":finally" and ignore it. - eap->errmsg = _("E607: multiple :finally"); - return; - } - rewind_conditionals(cstack, idx, CSF_WHILE | CSF_FOR, - &cstack->cs_looplevel); + if (!(cstack->cs_flags[cstack->cs_idx] & CSF_TRY)) { + eap->errmsg = get_end_emsg(cstack); + // Make this error pending, so that the commands in the following + // finally clause can be executed. This overrules also a pending + // ":continue", ":break", ":return", or ":finish". + pending = CSTP_ERROR; + } - // Don't do something when the corresponding try block never got active - // (because of an inactive surrounding conditional or after an error or - // interrupt or throw) or for a ":finally" without ":try" or a multiple - // ":finally". After every other error (did_emsg or the conditional - // errors detected above) or after an interrupt (got_int) or an - // exception (did_throw), the finally clause must be executed. - skip = !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE); + if (cstack->cs_flags[idx] & CSF_FINALLY) { + // Give up for a multiple ":finally" and ignore it. + eap->errmsg = _("E607: multiple :finally"); + return; + } + rewind_conditionals(cstack, idx, CSF_WHILE | CSF_FOR, + &cstack->cs_looplevel); + + // Don't do something when the corresponding try block never got active + // (because of an inactive surrounding conditional or after an error or + // interrupt or throw) or for a ":finally" without ":try" or a multiple + // ":finally". After every other error (did_emsg or the conditional + // errors detected above) or after an interrupt (got_int) or an + // exception (did_throw), the finally clause must be executed. + skip = !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE); + + if (!skip) { + // When debugging or a breakpoint was encountered, display the + // debug prompt (if not already done). The user then knows that the + // finally clause is executed. + if (dbg_check_skipped(eap)) { + // Handle a ">quit" debug command as if an interrupt had + // occurred before the ":finally". That is, discard the + // original exception and replace it by an interrupt + // exception. + (void)do_intthrow(cstack); + } - if (!skip) { - // When debugging or a breakpoint was encountered, display the - // debug prompt (if not already done). The user then knows that the - // finally clause is executed. - if (dbg_check_skipped(eap)) { - // Handle a ">quit" debug command as if an interrupt had - // occurred before the ":finally". That is, discard the - // original exception and replace it by an interrupt - // exception. - (void)do_intthrow(cstack); + // If there is a preceding catch clause and it caught the exception, + // finish the exception now. This happens also after errors except + // when this is a multiple ":finally" or one not within a ":try". + // After an error or interrupt, this also discards a pending + // ":continue", ":break", ":finish", or ":return" from the preceding + // try block or catch clause. + cleanup_conditionals(cstack, CSF_TRY, false); + + // Make did_emsg, got_int, did_throw pending. If set, they overrule + // a pending ":continue", ":break", ":return", or ":finish". Then + // we have particularly to discard a pending return value (as done + // by the call to cleanup_conditionals() above when did_emsg or + // got_int is set). The pending values are restored by the + // ":endtry", except if there is a new error, interrupt, exception, + // ":continue", ":break", ":return", or ":finish" in the following + // finally clause. A missing ":endwhile", ":endfor" or ":endif" + // detected here is treated as if did_emsg and did_throw had + // already been set, respectively in case that the error is not + // converted to an exception, did_throw had already been unset. + // We must not set did_emsg here since that would suppress the + // error message. + if (pending == CSTP_ERROR || did_emsg || got_int || did_throw) { + if (cstack->cs_pending[cstack->cs_idx] == CSTP_RETURN) { + report_discard_pending(CSTP_RETURN, + cstack->cs_rettv[cstack->cs_idx]); + discard_pending_return(cstack->cs_rettv[cstack->cs_idx]); } - - // If there is a preceding catch clause and it caught the exception, - // finish the exception now. This happens also after errors except - // when this is a multiple ":finally" or one not within a ":try". - // After an error or interrupt, this also discards a pending - // ":continue", ":break", ":finish", or ":return" from the preceding - // try block or catch clause. - cleanup_conditionals(cstack, CSF_TRY, false); - - // Make did_emsg, got_int, did_throw pending. If set, they overrule - // a pending ":continue", ":break", ":return", or ":finish". Then - // we have particularly to discard a pending return value (as done - // by the call to cleanup_conditionals() above when did_emsg or - // got_int is set). The pending values are restored by the - // ":endtry", except if there is a new error, interrupt, exception, - // ":continue", ":break", ":return", or ":finish" in the following - // finally clause. A missing ":endwhile", ":endfor" or ":endif" - // detected here is treated as if did_emsg and did_throw had - // already been set, respectively in case that the error is not - // converted to an exception, did_throw had already been unset. - // We must not set did_emsg here since that would suppress the - // error message. - if (pending == CSTP_ERROR || did_emsg || got_int || did_throw) { - if (cstack->cs_pending[cstack->cs_idx] == CSTP_RETURN) { - report_discard_pending(CSTP_RETURN, - cstack->cs_rettv[cstack->cs_idx]); - discard_pending_return(cstack->cs_rettv[cstack->cs_idx]); - } - if (pending == CSTP_ERROR && !did_emsg) { - pending |= (THROW_ON_ERROR ? CSTP_THROW : 0); - } else { - pending |= (did_throw ? CSTP_THROW : 0); - } - pending |= did_emsg ? CSTP_ERROR : 0; - pending |= got_int ? CSTP_INTERRUPT : 0; - assert(pending >= CHAR_MIN && pending <= CHAR_MAX); - cstack->cs_pending[cstack->cs_idx] = (char)pending; - - // It's mandatory that the current exception is stored in the - // cstack so that it can be rethrown at the ":endtry" or be - // discarded if the finally clause is left by a ":continue", - // ":break", ":return", ":finish", error, interrupt, or another - // exception. When emsg() is called for a missing ":endif" or - // a missing ":endwhile"/":endfor" detected here, the - // exception will be discarded. - if (did_throw && cstack->cs_exception[cstack->cs_idx] != current_exception) { - internal_error("ex_finally()"); - } + if (pending == CSTP_ERROR && !did_emsg) { + pending |= (THROW_ON_ERROR ? CSTP_THROW : 0); + } else { + pending |= (did_throw ? CSTP_THROW : 0); + } + pending |= did_emsg ? CSTP_ERROR : 0; + pending |= got_int ? CSTP_INTERRUPT : 0; + assert(pending >= CHAR_MIN && pending <= CHAR_MAX); + cstack->cs_pending[cstack->cs_idx] = (char)pending; + + // It's mandatory that the current exception is stored in the + // cstack so that it can be rethrown at the ":endtry" or be + // discarded if the finally clause is left by a ":continue", + // ":break", ":return", ":finish", error, interrupt, or another + // exception. When emsg() is called for a missing ":endif" or + // a missing ":endwhile"/":endfor" detected here, the + // exception will be discarded. + if (did_throw && cstack->cs_exception[cstack->cs_idx] != current_exception) { + internal_error("ex_finally()"); } - - // Set CSL_HAD_FINA, so do_cmdline() will reset did_emsg, - // got_int, and did_throw and make the finally clause active. - // This will happen after emsg() has been called for a missing - // ":endif" or a missing ":endwhile"/":endfor" detected here, so - // that the following finally clause will be executed even then. - cstack->cs_lflags |= CSL_HAD_FINA; } + + // Set CSL_HAD_FINA, so do_cmdline() will reset did_emsg, + // got_int, and did_throw and make the finally clause active. + // This will happen after emsg() has been called for a missing + // ":endif" or a missing ":endwhile"/":endfor" detected here, so + // that the following finally clause will be executed even then. + cstack->cs_lflags |= CSL_HAD_FINA; } } @@ -1534,165 +1533,167 @@ void ex_endtry(exarg_T *eap) void *rettv = NULL; cstack_T *const cstack = eap->cstack; - if (cstack->cs_trylevel <= 0 || cstack->cs_idx < 0) { + for (idx = cstack->cs_idx; idx >= 0; idx--) { + if (cstack->cs_flags[idx] & CSF_TRY) { + break; + } + } + if (cstack->cs_trylevel <= 0 || idx < 0) { eap->errmsg = _("E602: :endtry without :try"); - } else { - // Don't do something after an error, interrupt or throw in the try - // block, catch clause, or finally clause preceding this ":endtry" or - // when an error or interrupt occurred after a ":continue", ":break", - // ":return", or ":finish" in a try block or catch clause preceding this - // ":endtry" or when the try block never got active (because of an - // inactive surrounding conditional or after an error or interrupt or - // throw) or when there is a surrounding conditional and it has been - // made inactive by a ":continue", ":break", ":return", or ":finish" in - // the finally clause. The latter case need not be tested since then - // anything pending has already been discarded. - bool skip = did_emsg || got_int || did_throw || !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE); + return; + } - if (!(cstack->cs_flags[cstack->cs_idx] & CSF_TRY)) { - eap->errmsg = get_end_emsg(cstack); + // Don't do something after an error, interrupt or throw in the try + // block, catch clause, or finally clause preceding this ":endtry" or + // when an error or interrupt occurred after a ":continue", ":break", + // ":return", or ":finish" in a try block or catch clause preceding this + // ":endtry" or when the try block never got active (because of an + // inactive surrounding conditional or after an error or interrupt or + // throw) or when there is a surrounding conditional and it has been + // made inactive by a ":continue", ":break", ":return", or ":finish" in + // the finally clause. The latter case need not be tested since then + // anything pending has already been discarded. + bool skip = did_emsg || got_int || did_throw || !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE); - // Find the matching ":try" and report what's missing. - idx = cstack->cs_idx; - do { - idx--; - } while (idx > 0 && !(cstack->cs_flags[idx] & CSF_TRY)); - rewind_conditionals(cstack, idx, CSF_WHILE | CSF_FOR, - &cstack->cs_looplevel); - skip = true; + if (!(cstack->cs_flags[cstack->cs_idx] & CSF_TRY)) { + eap->errmsg = get_end_emsg(cstack); - // If an exception is being thrown, discard it to prevent it from - // being rethrown at the end of this function. It would be - // discarded by the error message, anyway. Resets did_throw. - // This does not affect the script termination due to the error - // since "trylevel" is decremented after emsg() has been called. - if (did_throw) { - discard_current_exception(); - } + // Find the matching ":try" and report what's missing. + rewind_conditionals(cstack, idx, CSF_WHILE | CSF_FOR, + &cstack->cs_looplevel); + skip = true; - // report eap->errmsg, also when there already was an error - did_emsg = false; - } else { - idx = cstack->cs_idx; - - // If we stopped with the exception currently being thrown at this - // try conditional since we didn't know that it doesn't have - // a finally clause, we need to rethrow it after closing the try - // conditional. - if (did_throw - && (cstack->cs_flags[idx] & CSF_TRUE) - && !(cstack->cs_flags[idx] & CSF_FINALLY)) { - rethrow = true; - } + // If an exception is being thrown, discard it to prevent it from + // being rethrown at the end of this function. It would be + // discarded by the error message, anyway. Resets did_throw. + // This does not affect the script termination due to the error + // since "trylevel" is decremented after emsg() has been called. + if (did_throw) { + discard_current_exception(); } - // If there was no finally clause, show the user when debugging or - // a breakpoint was encountered that the end of the try conditional has - // been reached: display the debug prompt (if not already done). Do - // this on normal control flow or when an exception was thrown, but not - // on an interrupt or error not converted to an exception or when - // a ":break", ":continue", ":return", or ":finish" is pending. These - // actions are carried out immediately. - if ((rethrow || (!skip - && !(cstack->cs_flags[idx] & CSF_FINALLY) - && !cstack->cs_pending[idx])) - && dbg_check_skipped(eap)) { - // Handle a ">quit" debug command as if an interrupt had occurred - // before the ":endtry". That is, throw an interrupt exception and - // set "skip" and "rethrow". - if (got_int) { - skip = true; - (void)do_intthrow(cstack); - // The do_intthrow() call may have reset did_throw or - // cstack->cs_pending[idx]. - rethrow = false; - if (did_throw && !(cstack->cs_flags[idx] & CSF_FINALLY)) { - rethrow = true; - } + // report eap->errmsg, also when there already was an error + did_emsg = false; + } else { + idx = cstack->cs_idx; + + // If we stopped with the exception currently being thrown at this + // try conditional since we didn't know that it doesn't have + // a finally clause, we need to rethrow it after closing the try + // conditional. + if (did_throw + && (cstack->cs_flags[idx] & CSF_TRUE) + && !(cstack->cs_flags[idx] & CSF_FINALLY)) { + rethrow = true; + } + } + + // If there was no finally clause, show the user when debugging or + // a breakpoint was encountered that the end of the try conditional has + // been reached: display the debug prompt (if not already done). Do + // this on normal control flow or when an exception was thrown, but not + // on an interrupt or error not converted to an exception or when + // a ":break", ":continue", ":return", or ":finish" is pending. These + // actions are carried out immediately. + if ((rethrow || (!skip + && !(cstack->cs_flags[idx] & CSF_FINALLY) + && !cstack->cs_pending[idx])) + && dbg_check_skipped(eap)) { + // Handle a ">quit" debug command as if an interrupt had occurred + // before the ":endtry". That is, throw an interrupt exception and + // set "skip" and "rethrow". + if (got_int) { + skip = true; + (void)do_intthrow(cstack); + // The do_intthrow() call may have reset did_throw or + // cstack->cs_pending[idx]. + rethrow = false; + if (did_throw && !(cstack->cs_flags[idx] & CSF_FINALLY)) { + rethrow = true; } } + } - // If a ":return" is pending, we need to resume it after closing the - // try conditional; remember the return value. If there was a finally - // clause making an exception pending, we need to rethrow it. Make it - // the exception currently being thrown. - if (!skip) { - pending = cstack->cs_pending[idx]; - cstack->cs_pending[idx] = CSTP_NONE; - if (pending == CSTP_RETURN) { - rettv = cstack->cs_rettv[idx]; - } else if (pending & CSTP_THROW) { - current_exception = cstack->cs_exception[idx]; - } + // If a ":return" is pending, we need to resume it after closing the + // try conditional; remember the return value. If there was a finally + // clause making an exception pending, we need to rethrow it. Make it + // the exception currently being thrown. + if (!skip) { + pending = cstack->cs_pending[idx]; + cstack->cs_pending[idx] = CSTP_NONE; + if (pending == CSTP_RETURN) { + rettv = cstack->cs_rettv[idx]; + } else if (pending & CSTP_THROW) { + current_exception = cstack->cs_exception[idx]; } + } - // Discard anything pending on an error, interrupt, or throw in the - // finally clause. If there was no ":finally", discard a pending - // ":continue", ":break", ":return", or ":finish" if an error or - // interrupt occurred afterwards, but before the ":endtry" was reached. - // If an exception was caught by the last of the catch clauses and there - // was no finally clause, finish the exception now. This happens also - // after errors except when this ":endtry" is not within a ":try". - // Restore "emsg_silent" if it has been reset by this try conditional. - (void)cleanup_conditionals(cstack, CSF_TRY | CSF_SILENT, true); + // Discard anything pending on an error, interrupt, or throw in the + // finally clause. If there was no ":finally", discard a pending + // ":continue", ":break", ":return", or ":finish" if an error or + // interrupt occurred afterwards, but before the ":endtry" was reached. + // If an exception was caught by the last of the catch clauses and there + // was no finally clause, finish the exception now. This happens also + // after errors except when this ":endtry" is not within a ":try". + // Restore "emsg_silent" if it has been reset by this try conditional. + (void)cleanup_conditionals(cstack, CSF_TRY | CSF_SILENT, true); - if (cstack->cs_idx >= 0 && (cstack->cs_flags[cstack->cs_idx] & CSF_TRY)) { - cstack->cs_idx--; - } - cstack->cs_trylevel--; + if (cstack->cs_idx >= 0 && (cstack->cs_flags[cstack->cs_idx] & CSF_TRY)) { + cstack->cs_idx--; + } + cstack->cs_trylevel--; - if (!skip) { - report_resume_pending(pending, - (pending == CSTP_RETURN) ? rettv : - (pending & CSTP_THROW) ? (void *)current_exception : NULL); - switch (pending) { - case CSTP_NONE: - break; + if (!skip) { + report_resume_pending(pending, + (pending == CSTP_RETURN) ? rettv : + (pending & CSTP_THROW) ? (void *)current_exception : NULL); + switch (pending) { + case CSTP_NONE: + break; - // Reactivate a pending ":continue", ":break", ":return", - // ":finish" from the try block or a catch clause of this try - // conditional. This is skipped, if there was an error in an - // (unskipped) conditional command or an interrupt afterwards - // or if the finally clause is present and executed a new error, - // interrupt, throw, ":continue", ":break", ":return", or - // ":finish". - case CSTP_CONTINUE: - ex_continue(eap); - break; - case CSTP_BREAK: - ex_break(eap); - break; - case CSTP_RETURN: - do_return(eap, false, false, rettv); - break; - case CSTP_FINISH: - do_finish(eap, false); - break; + // Reactivate a pending ":continue", ":break", ":return", + // ":finish" from the try block or a catch clause of this try + // conditional. This is skipped, if there was an error in an + // (unskipped) conditional command or an interrupt afterwards + // or if the finally clause is present and executed a new error, + // interrupt, throw, ":continue", ":break", ":return", or + // ":finish". + case CSTP_CONTINUE: + ex_continue(eap); + break; + case CSTP_BREAK: + ex_break(eap); + break; + case CSTP_RETURN: + do_return(eap, false, false, rettv); + break; + case CSTP_FINISH: + do_finish(eap, false); + break; - // When the finally clause was entered due to an error, - // interrupt or throw (as opposed to a ":continue", ":break", - // ":return", or ":finish"), restore the pending values of - // did_emsg, got_int, and did_throw. This is skipped, if there - // was a new error, interrupt, throw, ":continue", ":break", - // ":return", or ":finish". in the finally clause. - default: - if (pending & CSTP_ERROR) { - did_emsg = true; - } - if (pending & CSTP_INTERRUPT) { - got_int = true; - } - if (pending & CSTP_THROW) { - rethrow = true; - } - break; + // When the finally clause was entered due to an error, + // interrupt or throw (as opposed to a ":continue", ":break", + // ":return", or ":finish"), restore the pending values of + // did_emsg, got_int, and did_throw. This is skipped, if there + // was a new error, interrupt, throw, ":continue", ":break", + // ":return", or ":finish". in the finally clause. + default: + if (pending & CSTP_ERROR) { + did_emsg = true; + } + if (pending & CSTP_INTERRUPT) { + got_int = true; + } + if (pending & CSTP_THROW) { + rethrow = true; } + break; } + } - if (rethrow) { - // Rethrow the current exception (within this cstack). - do_throw(cstack); - } + if (rethrow) { + // Rethrow the current exception (within this cstack). + do_throw(cstack); } } diff --git a/src/nvim/help.c b/src/nvim/help.c index cdd930203f..2095925db3 100644 --- a/src/nvim/help.c +++ b/src/nvim/help.c @@ -743,28 +743,26 @@ void fix_help_buffer(void) // If foo.abx is found use it instead of foo.txt in // the same directory. for (int i1 = 0; i1 < fcount; i1++) { - for (int i2 = 0; i2 < fcount; i2++) { - if (i1 == i2) { - continue; - } - if (fnames[i1] == NULL || fnames[i2] == NULL) { + const char *const f1 = fnames[i1]; + const char *const t1 = path_tail(f1); + const char *const e1 = strrchr(t1, '.'); + if (path_fnamecmp(e1, ".txt") != 0 + && path_fnamecmp(e1, fname + 4) != 0) { + // Not .txt and not .abx, remove it. + XFREE_CLEAR(fnames[i1]); + continue; + } + + for (int i2 = i1 + 1; i2 < fcount; i2++) { + const char *const f2 = fnames[i2]; + if (f2 == NULL) { continue; } - const char *const f1 = fnames[i1]; - const char *const f2 = fnames[i2]; - const char *const t1 = path_tail(f1); const char *const t2 = path_tail(f2); - const char *const e1 = strrchr(t1, '.'); const char *const e2 = strrchr(t2, '.'); if (e1 == NULL || e2 == NULL) { continue; } - if (path_fnamecmp(e1, ".txt") != 0 - && path_fnamecmp(e1, fname + 4) != 0) { - // Not .txt and not .abx, remove it. - XFREE_CLEAR(fnames[i1]); - continue; - } if (e1 - f1 != e2 - f2 || path_fnamencmp(f1, f2, (size_t)(e1 - f1)) != 0) { continue; @@ -943,6 +941,7 @@ static void helptags_one(char *dir, const char *ext, const char *tagfname, bool } const char *const fname = files[fi] + dirlen + 1; + bool in_example = false; bool firstline = true; while (!vim_fgets(IObuff, IOSIZE, fd) && !got_int) { if (firstline) { @@ -973,6 +972,13 @@ static void helptags_one(char *dir, const char *ext, const char *tagfname, bool } firstline = false; } + if (in_example) { + // skip over example; a non-white in the first column ends it + if (vim_strchr(" \t\n\r", IObuff[0])) { + continue; + } + in_example = false; + } p1 = vim_strchr((char *)IObuff, '*'); // find first '*' while (p1 != NULL) { p2 = strchr((const char *)p1 + 1, '*'); // Find second '*'. @@ -992,7 +998,7 @@ static void helptags_one(char *dir, const char *ext, const char *tagfname, bool || s[1] == '\0')) { *p2 = '\0'; p1++; - size_t s_len= (size_t)(p2 - p1) + strlen(fname) + 2; + size_t s_len = (size_t)(p2 - p1) + strlen(fname) + 2; s = xmalloc(s_len); GA_APPEND(char *, &ga, s); snprintf(s, s_len, "%s\t%s", p1, fname); @@ -1003,6 +1009,11 @@ static void helptags_one(char *dir, const char *ext, const char *tagfname, bool } p1 = p2; } + size_t len = strlen(IObuff); + if ((len == 2 && strcmp(&IObuff[len - 2], ">\n") == 0) + || (len >= 3 && strcmp(&IObuff[len - 3], " >\n") == 0)) { + in_example = true; + } line_breakcheck(); } diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index 70584c2a6c..e58a8fa06a 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -1094,8 +1094,8 @@ static bool reg_match_visual(void) colnr_T start2, end2; colnr_T curswant; - // Check if the buffer is the current buffer. - if (rex.reg_buf != curbuf || VIsual.lnum == 0) { + // Check if the buffer is the current buffer and not using a string. + if (rex.reg_buf != curbuf || VIsual.lnum == 0 || !REG_MULTI) { return false; } diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim index 15f66fc1ad..3c5699af73 100644 --- a/src/nvim/testdir/runtest.vim +++ b/src/nvim/testdir/runtest.vim @@ -105,7 +105,7 @@ set nomore lang mess C " Nvim: append runtime from build dir, which contains the generated doc/tags. -let &runtimepath .= ','.expand($BUILD_DIR).'/runtime/' +let &runtimepath ..= ',' .. expand($BUILD_DIR) .. '/runtime/' let s:t_bold = &t_md let s:t_normal = &t_me diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index 1308beeae5..f3594d3cdc 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -2,6 +2,9 @@ source shared.vim source check.vim +source term_util.vim +source screendump.vim +source vim9.vim " Must be done first, since the alternate buffer must be unset. func Test_00_bufexists() @@ -2518,7 +2521,7 @@ func Test_builtin_check() vim9script var s:trim = (x) => " " .. x END - " call CheckScriptFailure(lines, 'E704:') + call CheckScriptFailure(lines, 'E704:') call assert_fails('call extend(g:, #{foo: { -> "foo" }})', 'E704:') let g:bar = 123 diff --git a/src/nvim/testdir/test_help.vim b/src/nvim/testdir/test_help.vim index e30d305703..08dd3dcb9a 100644 --- a/src/nvim/testdir/test_help.vim +++ b/src/nvim/testdir/test_help.vim @@ -1,6 +1,22 @@ " Tests for :help source check.vim +source vim9.vim + +func SetUp() + let s:vimruntime = $VIMRUNTIME + let s:runtimepath = &runtimepath + " Set $VIMRUNTIME to $BUILD_DIR/runtime and remove the original $VIMRUNTIME + " path from &runtimepath so that ":h local-additions" won't pick up builtin + " help files. + let $VIMRUNTIME = expand($BUILD_DIR) .. '/runtime' + set runtimepath-=../../../runtime +endfunc + +func TearDown() + let $VIMRUNTIME = s:vimruntime + let &runtimepath = s:runtimepath +endfunc func Test_help_restore_snapshot() help @@ -81,16 +97,42 @@ func Test_help_local_additions() call writefile(['*mydoc-ext.txt* my extended awesome doc'], 'Xruntime/doc/mydoc-ext.txt') let rtp_save = &rtp set rtp+=./Xruntime - help - 1 - call search('mydoc.txt') - call assert_equal('|mydoc.txt| my awesome doc', getline('.')) - 1 - call search('mydoc-ext.txt') - call assert_equal('|mydoc-ext.txt| my extended awesome doc', getline('.')) + help local-additions + let lines = getline(line(".") + 1, search("^$") - 1) + call assert_equal([ + \ '|mydoc-ext.txt| my extended awesome doc', + \ '|mydoc.txt| my awesome doc' + \ ], lines) + call delete('Xruntime/doc/mydoc-ext.txt') + close + + call mkdir('Xruntime-ja/doc', 'p') + call writefile(["local-additions\thelp.jax\t/*local-additions*"], 'Xruntime-ja/doc/tags-ja') + call writefile(['*help.txt* This is jax file', '', + \ 'LOCAL ADDITIONS: *local-additions*', ''], 'Xruntime-ja/doc/help.jax') + call writefile(['*work.txt* This is jax file'], 'Xruntime-ja/doc/work.jax') + call writefile(['*work2.txt* This is jax file'], 'Xruntime-ja/doc/work2.jax') + set rtp+=./Xruntime-ja + + help local-additions@en + let lines = getline(line(".") + 1, search("^$") - 1) + call assert_equal([ + \ '|mydoc.txt| my awesome doc' + \ ], lines) + close + + help local-additions@ja + let lines = getline(line(".") + 1, search("^$") - 1) + call assert_equal([ + \ '|mydoc.txt| my awesome doc', + \ '|help.txt| This is jax file', + \ '|work.txt| This is jax file', + \ '|work2.txt| This is jax file', + \ ], lines) close call delete('Xruntime', 'rf') + call delete('Xruntime-ja', 'rf') let &rtp = rtp_save endfunc @@ -114,6 +156,13 @@ func Test_helptag_cmd() call assert_equal(["help-tags\ttags\t1"], readfile('Xdir/tags')) call delete('Xdir/tags') + " Test parsing tags + call writefile(['*tag1*', 'Example: >', ' *notag*', 'Example end: *tag2*'], + \ 'Xdir/a/doc/sample.txt') + helptags Xdir + call assert_equal(["tag1\ta/doc/sample.txt\t/*tag1*", + \ "tag2\ta/doc/sample.txt\t/*tag2*"], readfile('Xdir/tags')) + " Duplicate tags in the help file call writefile(['*tag1*', '*tag1*', '*tag2*'], 'Xdir/a/doc/sample.txt') call assert_fails('helptags Xdir', 'E154:') @@ -166,5 +215,15 @@ func Test_help_long_argument() endtry endfunc +func Test_help_using_visual_match() + let lines =<< trim END + call setline(1, ' ') + /^ + exe "normal \<C-V>\<C-V>" + h5\%V] + END + call CheckScriptFailure(lines, 'E149:') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_help_tagjump.vim b/src/nvim/testdir/test_help_tagjump.vim index f81e4fb1ef..eae1a241e3 100644 --- a/src/nvim/testdir/test_help_tagjump.vim +++ b/src/nvim/testdir/test_help_tagjump.vim @@ -1,17 +1,5 @@ " Tests for :help! {subject} -func SetUp() - " v:progpath is …/build/bin/nvim and we need …/build/runtime - " to be added to &rtp - let builddir = fnamemodify(exepath(v:progpath), ':h:h') - let s:rtp = &rtp - let &rtp .= printf(',%s/runtime', builddir) -endfunc - -func TearDown() - let &rtp = s:rtp -endfunc - func Test_help_tagjump() help call assert_equal("help", &filetype) diff --git a/src/nvim/testdir/test_ins_complete.vim b/src/nvim/testdir/test_ins_complete.vim index 1811c82767..c1c78e9a8f 100644 --- a/src/nvim/testdir/test_ins_complete.vim +++ b/src/nvim/testdir/test_ins_complete.vim @@ -1535,7 +1535,7 @@ func Test_completefunc_callback() assert_equal([[1, ''], [0, 'three']], g:LocalCompleteFuncArgs) bw! END - " call CheckScriptSuccess(lines) + call CheckScriptSuccess(lines) " cleanup set completefunc& @@ -1792,7 +1792,7 @@ func Test_omnifunc_callback() assert_equal([[1, ''], [0, 'three']], g:LocalOmniFuncArgs) bw! END - " call CheckScriptSuccess(lines) + call CheckScriptSuccess(lines) " cleanup set omnifunc& @@ -2085,7 +2085,7 @@ func Test_thesaurusfunc_callback() assert_equal([[1, ''], [0, 'three']], g:LocalTsrFuncArgs) bw! END - " call CheckScriptSuccess(lines) + call CheckScriptSuccess(lines) " cleanup set thesaurusfunc& diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim index 7e8b8c5eef..c2ad49f0c9 100644 --- a/src/nvim/testdir/test_normal.vim +++ b/src/nvim/testdir/test_normal.vim @@ -695,7 +695,7 @@ func Test_opfunc_callback() assert_equal(['char'], g:LocalOpFuncArgs) bw! END - " call CheckScriptSuccess(lines) + call CheckScriptSuccess(lines) " setting 'opfunc' to a script local function outside of a script context " should fail diff --git a/src/nvim/testdir/test_tagfunc.vim b/src/nvim/testdir/test_tagfunc.vim index 93b9c67b25..cba96d3504 100644 --- a/src/nvim/testdir/test_tagfunc.vim +++ b/src/nvim/testdir/test_tagfunc.vim @@ -380,7 +380,7 @@ func Test_tagfunc_callback() assert_equal(['a12', '', {}], g:LocalTagFuncArgs) bw! END - " call CheckScriptSuccess(lines) + call CheckScriptSuccess(lines) " cleanup delfunc TagFunc1 diff --git a/src/nvim/testdir/test_trycatch.vim b/src/nvim/testdir/test_trycatch.vim index 8a1d2d3fa7..ef20e03126 100644 --- a/src/nvim/testdir/test_trycatch.vim +++ b/src/nvim/testdir/test_trycatch.vim @@ -3,6 +3,7 @@ source check.vim source shared.vim +source vim9.vim "------------------------------------------------------------------------------- " Test environment {{{1 @@ -2007,6 +2008,27 @@ func Test_try_catch_errors() call assert_fails('try | for i in range(5) | endif | endtry', 'E580:') call assert_fails('try | while v:true | endtry', 'E170:') call assert_fails('try | if v:true | endtry', 'E171:') + + " this was using a negative index in cstack[] + let lines =<< trim END + try + for + if + endwhile + if + finally + END + call CheckScriptFailure(lines, 'E690:') + + let lines =<< trim END + try + for + if + endwhile + if + endtry + END + call CheckScriptFailure(lines, 'E690:') endfunc " Test for verbose messages with :try :catch, and :finally {{{1 diff --git a/src/nvim/testdir/vim9.vim b/src/nvim/testdir/vim9.vim index db74ce3b11..3c0ff2b2dd 100644 --- a/src/nvim/testdir/vim9.vim +++ b/src/nvim/testdir/vim9.vim @@ -2,6 +2,38 @@ " Use a different file name for each run. let s:sequence = 1 +func CheckScriptFailure(lines, error, lnum = -3) + if get(a:lines, 0, '') ==# 'vim9script' + return + endif + let cwd = getcwd() + let fname = 'XScriptFailure' .. s:sequence + let s:sequence += 1 + call writefile(a:lines, fname) + try + call assert_fails('so ' .. fname, a:error, a:lines, a:lnum) + finally + call chdir(cwd) + call delete(fname) + endtry +endfunc + +func CheckScriptSuccess(lines) + if get(a:lines, 0, '') ==# 'vim9script' + return + endif + let cwd = getcwd() + let fname = 'XScriptSuccess' .. s:sequence + let s:sequence += 1 + call writefile(a:lines, fname) + try + exe 'so ' .. fname + finally + call chdir(cwd) + call delete(fname) + endtry +endfunc + " Check that "lines" inside a legacy function has no error. func CheckLegacySuccess(lines) let cwd = getcwd() |