comparison src/userfunc.c @ 30065:6cf788ab844c v9.0.0370

patch 9.0.0370: cleaning up afterwards can make a function messy Commit: https://github.com/vim/vim/commit/1d84f7608f1e41dad03b8cc7925895437775f7c0 Author: Bram Moolenaar <Bram@vim.org> Date: Sat Sep 3 21:35:53 2022 +0100 patch 9.0.0370: cleaning up afterwards can make a function messy Problem: Cleaning up afterwards can make a function messy. Solution: Add the :defer command.
author Bram Moolenaar <Bram@vim.org>
date Sat, 03 Sep 2022 22:45:03 +0200
parents cd573d7bc30d
children d45ee1f829ba
comparison
equal deleted inserted replaced
30064:a8f1fbaa43c8 30065:6cf788ab844c
1726 if (p != name) 1726 if (p != name)
1727 vim_free(p); 1727 vim_free(p);
1728 } 1728 }
1729 1729
1730 /* 1730 /*
1731 * Allocate a variable for the result of a function. 1731 * Get function arguments at "*arg" and advance it.
1732 * Return them in "*argvars[MAX_FUNC_ARGS + 1]" and the count in "argcount".
1733 */
1734 static int
1735 get_func_arguments(
1736 char_u **arg,
1737 evalarg_T *evalarg,
1738 int partial_argc,
1739 typval_T *argvars,
1740 int *argcount)
1741 {
1742 char_u *argp = *arg;
1743 int ret = OK;
1744 int vim9script = in_vim9script();
1745 int evaluate = evalarg == NULL
1746 ? FALSE : (evalarg->eval_flags & EVAL_EVALUATE);
1747
1748 while (*argcount < MAX_FUNC_ARGS - partial_argc)
1749 {
1750 // skip the '(' or ',' and possibly line breaks
1751 argp = skipwhite_and_linebreak(argp + 1, evalarg);
1752
1753 if (*argp == ')' || *argp == ',' || *argp == NUL)
1754 break;
1755 if (eval1(&argp, &argvars[*argcount], evalarg) == FAIL)
1756 {
1757 ret = FAIL;
1758 break;
1759 }
1760 ++*argcount;
1761 // The comma should come right after the argument, but this wasn't
1762 // checked previously, thus only enforce it in Vim9 script.
1763 if (vim9script)
1764 {
1765 if (*argp != ',' && *skipwhite(argp) == ',')
1766 {
1767 if (evaluate)
1768 semsg(_(e_no_white_space_allowed_before_str_str),
1769 ",", argp);
1770 ret = FAIL;
1771 break;
1772 }
1773 }
1774 else
1775 argp = skipwhite(argp);
1776 if (*argp != ',')
1777 break;
1778 if (vim9script && !IS_WHITE_OR_NUL(argp[1]))
1779 {
1780 if (evaluate)
1781 semsg(_(e_white_space_required_after_str_str), ",", argp);
1782 ret = FAIL;
1783 break;
1784 }
1785 }
1786
1787 argp = skipwhite_and_linebreak(argp, evalarg);
1788 if (*argp == ')')
1789 ++argp;
1790 else
1791 ret = FAIL;
1792 *arg = argp;
1793 return ret;
1794 }
1795
1796 /*
1797 * Call a function and put the result in "rettv".
1732 * Return OK or FAIL. 1798 * Return OK or FAIL.
1733 */ 1799 */
1734 int 1800 int
1735 get_func_tv( 1801 get_func_tv(
1736 char_u *name, // name of the function 1802 char_u *name, // name of the function
1741 funcexe_T *funcexe) // various values 1807 funcexe_T *funcexe) // various values
1742 { 1808 {
1743 char_u *argp; 1809 char_u *argp;
1744 int ret = OK; 1810 int ret = OK;
1745 typval_T argvars[MAX_FUNC_ARGS + 1]; // vars for arguments 1811 typval_T argvars[MAX_FUNC_ARGS + 1]; // vars for arguments
1746 int argcount = 0; // number of arguments found 1812 int argcount = 0; // number of arguments found
1747 int vim9script = in_vim9script(); 1813 int vim9script = in_vim9script();
1748 int evaluate = evalarg == NULL 1814 int evaluate = evalarg == NULL
1749 ? FALSE : (evalarg->eval_flags & EVAL_EVALUATE); 1815 ? FALSE : (evalarg->eval_flags & EVAL_EVALUATE);
1750 1816
1751 /*
1752 * Get the arguments.
1753 */
1754 argp = *arg; 1817 argp = *arg;
1755 while (argcount < MAX_FUNC_ARGS - (funcexe->fe_partial == NULL ? 0 1818 ret = get_func_arguments(&argp, evalarg,
1756 : funcexe->fe_partial->pt_argc)) 1819 (funcexe->fe_partial == NULL ? 0 : funcexe->fe_partial->pt_argc),
1757 { 1820 argvars, &argcount);
1758 // skip the '(' or ',' and possibly line breaks
1759 argp = skipwhite_and_linebreak(argp + 1, evalarg);
1760
1761 if (*argp == ')' || *argp == ',' || *argp == NUL)
1762 break;
1763 if (eval1(&argp, &argvars[argcount], evalarg) == FAIL)
1764 {
1765 ret = FAIL;
1766 break;
1767 }
1768 ++argcount;
1769 // The comma should come right after the argument, but this wasn't
1770 // checked previously, thus only enforce it in Vim9 script.
1771 if (vim9script)
1772 {
1773 if (*argp != ',' && *skipwhite(argp) == ',')
1774 {
1775 if (evaluate)
1776 semsg(_(e_no_white_space_allowed_before_str_str),
1777 ",", argp);
1778 ret = FAIL;
1779 break;
1780 }
1781 }
1782 else
1783 argp = skipwhite(argp);
1784 if (*argp != ',')
1785 break;
1786 if (vim9script && !IS_WHITE_OR_NUL(argp[1]))
1787 {
1788 if (evaluate)
1789 semsg(_(e_white_space_required_after_str_str), ",", argp);
1790 ret = FAIL;
1791 break;
1792 }
1793 }
1794 argp = skipwhite_and_linebreak(argp, evalarg);
1795 if (*argp == ')')
1796 ++argp;
1797 else
1798 ret = FAIL;
1799 1821
1800 if (ret == OK) 1822 if (ret == OK)
1801 { 1823 {
1802 int i = 0; 1824 int i = 0;
1803 int did_emsg_before = did_emsg; 1825 int did_emsg_before = did_emsg;
2881 } 2903 }
2882 else 2904 else
2883 // call do_cmdline() to execute the lines 2905 // call do_cmdline() to execute the lines
2884 do_cmdline(NULL, get_func_line, (void *)fc, 2906 do_cmdline(NULL, get_func_line, (void *)fc,
2885 DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT); 2907 DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
2908
2909 // Invoke functions added with ":defer".
2910 handle_defer();
2886 2911
2887 --RedrawingDisabled; 2912 --RedrawingDisabled;
2888 2913
2889 // when the function was aborted because of an error, return -1 2914 // when the function was aborted because of an error, return -1
2890 if ((did_emsg && (fp->uf_flags & FC_ABORT)) || rettv->v_type == VAR_UNKNOWN) 2915 if ((did_emsg && (fp->uf_flags & FC_ABORT)) || rettv->v_type == VAR_UNKNOWN)
5455 if (eap->skip) 5480 if (eap->skip)
5456 --emsg_skip; 5481 --emsg_skip;
5457 clear_evalarg(&evalarg, eap); 5482 clear_evalarg(&evalarg, eap);
5458 } 5483 }
5459 5484
5485 static int
5486 ex_call_inner(
5487 exarg_T *eap,
5488 char_u *name,
5489 char_u **arg,
5490 char_u *startarg,
5491 funcexe_T *funcexe_init,
5492 evalarg_T *evalarg)
5493 {
5494 linenr_T lnum;
5495 int doesrange;
5496 typval_T rettv;
5497 int failed = FALSE;
5498
5499 /*
5500 * When skipping, evaluate the function once, to find the end of the
5501 * arguments.
5502 * When the function takes a range, this is discovered after the first
5503 * call, and the loop is broken.
5504 */
5505 if (eap->skip)
5506 {
5507 ++emsg_skip;
5508 lnum = eap->line2; // do it once, also with an invalid range
5509 }
5510 else
5511 lnum = eap->line1;
5512 for ( ; lnum <= eap->line2; ++lnum)
5513 {
5514 funcexe_T funcexe;
5515
5516 if (!eap->skip && eap->addr_count > 0)
5517 {
5518 if (lnum > curbuf->b_ml.ml_line_count)
5519 {
5520 // If the function deleted lines or switched to another buffer
5521 // the line number may become invalid.
5522 emsg(_(e_invalid_range));
5523 break;
5524 }
5525 curwin->w_cursor.lnum = lnum;
5526 curwin->w_cursor.col = 0;
5527 curwin->w_cursor.coladd = 0;
5528 }
5529 *arg = startarg;
5530
5531 funcexe = *funcexe_init;
5532 funcexe.fe_doesrange = &doesrange;
5533 rettv.v_type = VAR_UNKNOWN; // clear_tv() uses this
5534 if (get_func_tv(name, -1, &rettv, arg, evalarg, &funcexe) == FAIL)
5535 {
5536 failed = TRUE;
5537 break;
5538 }
5539 if (has_watchexpr())
5540 dbg_check_breakpoint(eap);
5541
5542 // Handle a function returning a Funcref, Dictionary or List.
5543 if (handle_subscript(arg, NULL, &rettv,
5544 eap->skip ? NULL : &EVALARG_EVALUATE, TRUE) == FAIL)
5545 {
5546 failed = TRUE;
5547 break;
5548 }
5549
5550 clear_tv(&rettv);
5551 if (doesrange || eap->skip)
5552 break;
5553
5554 // Stop when immediately aborting on error, or when an interrupt
5555 // occurred or an exception was thrown but not caught.
5556 // get_func_tv() returned OK, so that the check for trailing
5557 // characters below is executed.
5558 if (aborting())
5559 break;
5560 }
5561 if (eap->skip)
5562 --emsg_skip;
5563 return failed;
5564 }
5565
5566 /*
5567 * Core part of ":defer func(arg)". "arg" points to the "(" and is advanced.
5568 * Returns FAIL or OK.
5569 */
5570 static int
5571 ex_defer_inner(char_u *name, char_u **arg, evalarg_T *evalarg)
5572 {
5573 typval_T argvars[MAX_FUNC_ARGS + 1]; // vars for arguments
5574 int argcount = 0; // number of arguments found
5575 defer_T *dr;
5576 int ret = FAIL;
5577 char_u *saved_name;
5578
5579 if (current_funccal == NULL)
5580 {
5581 semsg(_(e_str_not_inside_function), "defer");
5582 return FAIL;
5583 }
5584 if (get_func_arguments(arg, evalarg, FALSE, argvars, &argcount) == FAIL)
5585 goto theend;
5586 saved_name = vim_strsave(name);
5587 if (saved_name == NULL)
5588 goto theend;
5589
5590 if (current_funccal->fc_defer.ga_itemsize == 0)
5591 ga_init2(&current_funccal->fc_defer, sizeof(defer_T), 10);
5592 if (ga_grow(&current_funccal->fc_defer, 1) == FAIL)
5593 goto theend;
5594 dr = ((defer_T *)current_funccal->fc_defer.ga_data)
5595 + current_funccal->fc_defer.ga_len++;
5596 dr->dr_name = saved_name;
5597 dr->dr_argcount = argcount;
5598 while (argcount > 0)
5599 {
5600 --argcount;
5601 dr->dr_argvars[argcount] = argvars[argcount];
5602 }
5603 ret = OK;
5604
5605 theend:
5606 while (--argcount >= 0)
5607 clear_tv(&argvars[argcount]);
5608 return ret;
5609 }
5610
5611 /*
5612 * Invoked after a functions has finished: invoke ":defer" functions.
5613 */
5614 void
5615 handle_defer(void)
5616 {
5617 int idx;
5618
5619 for (idx = current_funccal->fc_defer.ga_len - 1; idx >= 0; --idx)
5620 {
5621 funcexe_T funcexe;
5622 typval_T rettv;
5623 defer_T *dr = ((defer_T *)current_funccal->fc_defer.ga_data) + idx;
5624 int i;
5625
5626 CLEAR_FIELD(funcexe);
5627 funcexe.fe_evaluate = TRUE;
5628
5629 rettv.v_type = VAR_UNKNOWN; // clear_tv() uses this
5630 call_func(dr->dr_name, -1, &rettv,
5631 dr->dr_argcount, dr->dr_argvars, &funcexe);
5632 clear_tv(&rettv);
5633 vim_free(dr->dr_name);
5634 for (i = dr->dr_argcount - 1; i >= 0; --i)
5635 clear_tv(&dr->dr_argvars[i]);
5636 }
5637 ga_clear(&current_funccal->fc_defer);
5638 }
5639
5460 /* 5640 /*
5461 * ":1,25call func(arg1, arg2)" function call. 5641 * ":1,25call func(arg1, arg2)" function call.
5642 * ":defer func(arg1, arg2)" deferred function call.
5462 */ 5643 */
5463 void 5644 void
5464 ex_call(exarg_T *eap) 5645 ex_call(exarg_T *eap)
5465 { 5646 {
5466 char_u *arg = eap->arg; 5647 char_u *arg = eap->arg;
5467 char_u *startarg; 5648 char_u *startarg;
5468 char_u *name; 5649 char_u *name;
5469 char_u *tofree; 5650 char_u *tofree;
5470 int len; 5651 int len;
5471 typval_T rettv;
5472 linenr_T lnum;
5473 int doesrange;
5474 int failed = FALSE; 5652 int failed = FALSE;
5475 funcdict_T fudi; 5653 funcdict_T fudi;
5476 partial_T *partial = NULL; 5654 partial_T *partial = NULL;
5477 evalarg_T evalarg; 5655 evalarg_T evalarg;
5478 type_T *type = NULL; 5656 type_T *type = NULL;
5480 int vim9script = in_vim9script(); 5658 int vim9script = in_vim9script();
5481 5659
5482 fill_evalarg_from_eap(&evalarg, eap, eap->skip); 5660 fill_evalarg_from_eap(&evalarg, eap, eap->skip);
5483 if (eap->skip) 5661 if (eap->skip)
5484 { 5662 {
5663 typval_T rettv;
5664
5485 // trans_function_name() doesn't work well when skipping, use eval0() 5665 // trans_function_name() doesn't work well when skipping, use eval0()
5486 // instead to skip to any following command, e.g. for: 5666 // instead to skip to any following command, e.g. for:
5487 // :if 0 | call dict.foo().bar() | endif 5667 // :if 0 | call dict.foo().bar() | endif
5488 ++emsg_skip; 5668 ++emsg_skip;
5489 if (eval0(eap->arg, &rettv, eap, &evalarg) != FAIL) 5669 if (eval0(eap->arg, &rettv, eap, &evalarg) != FAIL)
5529 { 5709 {
5530 semsg(_(e_no_white_space_allowed_before_str_str), "(", eap->arg); 5710 semsg(_(e_no_white_space_allowed_before_str_str), "(", eap->arg);
5531 goto end; 5711 goto end;
5532 } 5712 }
5533 5713
5534 /* 5714 if (eap->cmdidx == CMD_defer)
5535 * When skipping, evaluate the function once, to find the end of the 5715 {
5536 * arguments. 5716 arg = startarg;
5537 * When the function takes a range, this is discovered after the first 5717 failed = ex_defer_inner(name, &arg, &evalarg) == FAIL;
5538 * call, and the loop is broken.
5539 */
5540 if (eap->skip)
5541 {
5542 ++emsg_skip;
5543 lnum = eap->line2; // do it once, also with an invalid range
5544 } 5718 }
5545 else 5719 else
5546 lnum = eap->line1;
5547 for ( ; lnum <= eap->line2; ++lnum)
5548 { 5720 {
5549 funcexe_T funcexe; 5721 funcexe_T funcexe;
5550 5722
5551 if (!eap->skip && eap->addr_count > 0)
5552 {
5553 if (lnum > curbuf->b_ml.ml_line_count)
5554 {
5555 // If the function deleted lines or switched to another buffer
5556 // the line number may become invalid.
5557 emsg(_(e_invalid_range));
5558 break;
5559 }
5560 curwin->w_cursor.lnum = lnum;
5561 curwin->w_cursor.col = 0;
5562 curwin->w_cursor.coladd = 0;
5563 }
5564 arg = startarg;
5565
5566 CLEAR_FIELD(funcexe); 5723 CLEAR_FIELD(funcexe);
5724 funcexe.fe_check_type = type;
5725 funcexe.fe_partial = partial;
5726 funcexe.fe_selfdict = fudi.fd_dict;
5567 funcexe.fe_firstline = eap->line1; 5727 funcexe.fe_firstline = eap->line1;
5568 funcexe.fe_lastline = eap->line2; 5728 funcexe.fe_lastline = eap->line2;
5569 funcexe.fe_doesrange = &doesrange; 5729 funcexe.fe_found_var = found_var;
5570 funcexe.fe_evaluate = !eap->skip; 5730 funcexe.fe_evaluate = !eap->skip;
5571 funcexe.fe_partial = partial; 5731 failed = ex_call_inner(eap, name, &arg, startarg, &funcexe, &evalarg);
5572 funcexe.fe_selfdict = fudi.fd_dict; 5732 }
5573 funcexe.fe_check_type = type;
5574 funcexe.fe_found_var = found_var;
5575 rettv.v_type = VAR_UNKNOWN; // clear_tv() uses this
5576 if (get_func_tv(name, -1, &rettv, &arg, &evalarg, &funcexe) == FAIL)
5577 {
5578 failed = TRUE;
5579 break;
5580 }
5581 if (has_watchexpr())
5582 dbg_check_breakpoint(eap);
5583
5584 // Handle a function returning a Funcref, Dictionary or List.
5585 if (handle_subscript(&arg, NULL, &rettv,
5586 eap->skip ? NULL : &EVALARG_EVALUATE, TRUE) == FAIL)
5587 {
5588 failed = TRUE;
5589 break;
5590 }
5591
5592 clear_tv(&rettv);
5593 if (doesrange || eap->skip)
5594 break;
5595
5596 // Stop when immediately aborting on error, or when an interrupt
5597 // occurred or an exception was thrown but not caught.
5598 // get_func_tv() returned OK, so that the check for trailing
5599 // characters below is executed.
5600 if (aborting())
5601 break;
5602 }
5603 if (eap->skip)
5604 --emsg_skip;
5605 5733
5606 // When inside :try we need to check for following "| catch" or "| endtry". 5734 // When inside :try we need to check for following "| catch" or "| endtry".
5607 // Not when there was an error, but do check if an exception was thrown. 5735 // Not when there was an error, but do check if an exception was thrown.
5608 if ((!aborting() || did_throw) 5736 if ((!aborting() || did_throw) && (!failed || eap->cstack->cs_trylevel > 0))
5609 && (!failed || eap->cstack->cs_trylevel > 0))
5610 { 5737 {
5611 // Check for trailing illegal characters and a following command. 5738 // Check for trailing illegal characters and a following command.
5612 arg = skipwhite(arg); 5739 arg = skipwhite(arg);
5613 if (!ends_excmd2(eap->arg, arg)) 5740 if (!ends_excmd2(eap->arg, arg))
5614 { 5741 {