To: vim_dev@googlegroups.com Subject: Patch 8.1.0037 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.1.0037 Problem: Cannot easily append lines to another buffer. Solution: Add appendbufline(). Files: runtime/doc/eval.txt, src/evalfunc.c, src/testdir/test_bufline.vim, src/testdir/test_edit.vim *** ../vim-8.1.0036/runtime/doc/eval.txt 2018-06-03 14:42:17.824505143 +0200 --- runtime/doc/eval.txt 2018-06-06 20:58:47.732807369 +0200 *************** *** 2560,2565 **** --- 2560,2580 ---- 0 for success. Example: > :let failed = append(line('$'), "# THE END") :let failed = append(0, ["Chapter 1", "the beginning"]) + + appendbufline({expr}, {lnum}, {text}) *appendbufline()* + Like |append()| but append the text in buffer {expr}. + + For the use of {expr}, see |bufname()|. + + {lnum} is used like with |append()|. Note that using |line()| + would use the current buffer, not the one appending to. + Use "$" to append at the end of the buffer. + + On success 0 is returned, on failure 1 is returned. + + If {expr} is not a valid buffer or {lnum} is not valid, an + error message is given. Example: > + :let failed = appendbufline(13, 0, "# THE START") < *argc()* argc() The result is the number of files in the argument list of the *** ../vim-8.1.0036/src/evalfunc.c 2018-06-03 14:42:17.844505109 +0200 --- src/evalfunc.c 2018-06-06 20:58:47.740807362 +0200 *************** *** 40,45 **** --- 40,46 ---- static void f_add(typval_T *argvars, typval_T *rettv); static void f_and(typval_T *argvars, typval_T *rettv); static void f_append(typval_T *argvars, typval_T *rettv); + static void f_appendbufline(typval_T *argvars, typval_T *rettv); static void f_argc(typval_T *argvars, typval_T *rettv); static void f_argidx(typval_T *argvars, typval_T *rettv); static void f_arglistid(typval_T *argvars, typval_T *rettv); *************** *** 487,492 **** --- 488,494 ---- {"add", 2, 2, f_add}, {"and", 2, 2, f_and}, {"append", 2, 2, f_append}, + {"appendbufline", 3, 3, f_appendbufline}, {"argc", 0, 0, f_argc}, {"argidx", 0, 0, f_argidx}, {"arglistid", 0, 2, f_arglistid}, *************** *** 1192,1261 **** } /* ! * "append(lnum, string/list)" function */ static void ! f_append(typval_T *argvars, typval_T *rettv) { ! long lnum; ! char_u *line; list_T *l = NULL; listitem_T *li = NULL; - typval_T *tv; long added = 0; ! /* When coming here from Insert mode, sync undo, so that this can be ! * undone separately from what was previously inserted. */ ! if (u_sync_once == 2) { ! u_sync_once = 1; /* notify that u_sync() was called */ ! u_sync(TRUE); } ! lnum = get_tv_lnum(argvars); ! if (lnum >= 0 ! && lnum <= curbuf->b_ml.ml_line_count ! && u_save(lnum, lnum + 1) == OK) { ! if (argvars[1].v_type == VAR_LIST) ! { ! l = argvars[1].vval.v_list; ! if (l == NULL) ! return; ! li = l->lv_first; ! } ! for (;;) { ! if (l == NULL) ! tv = &argvars[1]; /* append a string */ ! else if (li == NULL) ! break; /* end of list */ ! else ! tv = &li->li_tv; /* append item from list */ ! line = get_tv_string_chk(tv); ! if (line == NULL) /* type error */ { ! rettv->vval.v_number = 1; /* Failed */ break; } ! ml_append(lnum + added, line, (colnr_T)0, FALSE); ! ++added; ! if (l == NULL) break; li = li->li_next; } ! appended_lines_mark(lnum, added); ! if (curwin->w_cursor.lnum > lnum) ! curwin->w_cursor.lnum += added; #ifdef FEAT_JOB_CHANNEL if (bt_prompt(curbuf) && (State & INSERT)) // show the line with the prompt update_topline(); #endif } else ! rettv->vval.v_number = 1; /* Failed */ } /* --- 1194,1378 ---- } /* ! * Get the lnum from the first argument. ! * Also accepts "$", then "buf" is used. ! * Returns 0 on error. ! */ ! static linenr_T ! get_tv_lnum_buf(typval_T *argvars, buf_T *buf) ! { ! if (argvars[0].v_type == VAR_STRING ! && argvars[0].vval.v_string != NULL ! && argvars[0].vval.v_string[0] == '$' ! && buf != NULL) ! return buf->b_ml.ml_line_count; ! return (linenr_T)get_tv_number_chk(&argvars[0], NULL); ! } ! ! /* ! * Set line or list of lines in buffer "buf". */ static void ! set_buffer_lines( ! buf_T *buf, ! linenr_T lnum_arg, ! int append, ! typval_T *lines, ! typval_T *rettv) { ! linenr_T lnum = lnum_arg + (append ? 1 : 0); ! char_u *line = NULL; list_T *l = NULL; listitem_T *li = NULL; long added = 0; + linenr_T append_lnum; + buf_T *curbuf_save = NULL; + win_T *curwin_save = NULL; + int is_curbuf = buf == curbuf; ! /* When using the current buffer ml_mfp will be set if needed. Useful when ! * setline() is used on startup. For other buffers the buffer must be ! * loaded. */ ! if (buf == NULL || (!is_curbuf && buf->b_ml.ml_mfp == NULL) || lnum < 1) { ! rettv->vval.v_number = 1; /* FAIL */ ! return; } ! if (!is_curbuf) { ! wininfo_T *wip; ! ! curbuf_save = curbuf; ! curwin_save = curwin; ! curbuf = buf; ! for (wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next) { ! if (wip->wi_win != NULL) { ! curwin = wip->wi_win; break; } ! } ! } ! ! if (append) ! // appendbufline() uses the line number below which we insert ! append_lnum = lnum - 1; ! else ! // setbufline() uses the line number above which we insert, we only ! // append if it's below the last line ! append_lnum = curbuf->b_ml.ml_line_count; ! ! if (lines->v_type == VAR_LIST) ! { ! l = lines->vval.v_list; ! li = l->lv_first; ! } ! else ! line = get_tv_string_chk(lines); ! ! /* default result is zero == OK */ ! for (;;) ! { ! if (l != NULL) ! { ! /* list argument, get next string */ ! if (li == NULL) break; + line = get_tv_string_chk(&li->li_tv); li = li->li_next; } ! rettv->vval.v_number = 1; /* FAIL */ ! if (line == NULL || lnum > curbuf->b_ml.ml_line_count + 1) ! break; ! ! /* When coming here from Insert mode, sync undo, so that this can be ! * undone separately from what was previously inserted. */ ! if (u_sync_once == 2) ! { ! u_sync_once = 1; /* notify that u_sync() was called */ ! u_sync(TRUE); ! } ! ! if (!append && lnum <= curbuf->b_ml.ml_line_count) ! { ! /* existing line, replace it */ ! if (u_savesub(lnum) == OK && ml_replace(lnum, line, TRUE) == OK) ! { ! changed_bytes(lnum, 0); ! if (is_curbuf && lnum == curwin->w_cursor.lnum) ! check_cursor_col(); ! rettv->vval.v_number = 0; /* OK */ ! } ! } ! else if (added > 0 || u_save(lnum - 1, lnum) == OK) ! { ! /* append the line */ ! ++added; ! if (ml_append(lnum - 1, line, (colnr_T)0, FALSE) == OK) ! rettv->vval.v_number = 0; /* OK */ ! } ! ! if (l == NULL) /* only one string argument */ ! break; ! ++lnum; ! } ! ! if (added > 0) ! { ! win_T *wp; ! tabpage_T *tp; ! ! appended_lines_mark(append_lnum, added); ! FOR_ALL_TAB_WINDOWS(tp, wp) ! if (wp->w_buffer == buf && wp->w_cursor.lnum > append_lnum) ! wp->w_cursor.lnum += added; ! check_cursor_col(); ! #ifdef FEAT_JOB_CHANNEL if (bt_prompt(curbuf) && (State & INSERT)) // show the line with the prompt update_topline(); #endif } + + if (!is_curbuf) + { + curbuf = curbuf_save; + curwin = curwin_save; + } + } + + /* + * "append(lnum, string/list)" function + */ + static void + f_append(typval_T *argvars, typval_T *rettv) + { + linenr_T lnum = get_tv_lnum(&argvars[0]); + + set_buffer_lines(curbuf, lnum, TRUE, &argvars[1], rettv); + } + + /* + * "appendbufline(buf, lnum, string/list)" function + */ + static void + f_appendbufline(typval_T *argvars, typval_T *rettv) + { + linenr_T lnum; + buf_T *buf; + + buf = get_buf_tv(&argvars[0], FALSE); + if (buf == NULL) + rettv->vval.v_number = 1; /* FAIL */ else ! { ! lnum = get_tv_lnum_buf(&argvars[1], buf); ! set_buffer_lines(buf, lnum, TRUE, &argvars[2], rettv); ! } } /* *************** *** 4276,4297 **** } /* - * Get the lnum from the first argument. - * Also accepts "$", then "buf" is used. - * Returns 0 on error. - */ - static linenr_T - get_tv_lnum_buf(typval_T *argvars, buf_T *buf) - { - if (argvars[0].v_type == VAR_STRING - && argvars[0].vval.v_string != NULL - && argvars[0].vval.v_string[0] == '$' - && buf != NULL) - return buf->b_ml.ml_line_count; - return (linenr_T)get_tv_number_chk(&argvars[0], NULL); - } - - /* * "getbufline()" function */ static void --- 4393,4398 ---- *************** *** 10226,10340 **** } /* - * Set line or list of lines in buffer "buf". - */ - static void - set_buffer_lines(buf_T *buf, linenr_T lnum, typval_T *lines, typval_T *rettv) - { - char_u *line = NULL; - list_T *l = NULL; - listitem_T *li = NULL; - long added = 0; - linenr_T lcount; - buf_T *curbuf_save = NULL; - win_T *curwin_save = NULL; - int is_curbuf = buf == curbuf; - - /* When using the current buffer ml_mfp will be set if needed. Useful when - * setline() is used on startup. For other buffers the buffer must be - * loaded. */ - if (buf == NULL || (!is_curbuf && buf->b_ml.ml_mfp == NULL) || lnum < 1) - { - rettv->vval.v_number = 1; /* FAIL */ - return; - } - - if (!is_curbuf) - { - wininfo_T *wip; - - curbuf_save = curbuf; - curwin_save = curwin; - curbuf = buf; - for (wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next) - { - if (wip->wi_win != NULL) - { - curwin = wip->wi_win; - break; - } - } - } - - lcount = curbuf->b_ml.ml_line_count; - - if (lines->v_type == VAR_LIST) - { - l = lines->vval.v_list; - li = l->lv_first; - } - else - line = get_tv_string_chk(lines); - - /* default result is zero == OK */ - for (;;) - { - if (l != NULL) - { - /* list argument, get next string */ - if (li == NULL) - break; - line = get_tv_string_chk(&li->li_tv); - li = li->li_next; - } - - rettv->vval.v_number = 1; /* FAIL */ - if (line == NULL || lnum > curbuf->b_ml.ml_line_count + 1) - break; - - /* When coming here from Insert mode, sync undo, so that this can be - * undone separately from what was previously inserted. */ - if (u_sync_once == 2) - { - u_sync_once = 1; /* notify that u_sync() was called */ - u_sync(TRUE); - } - - if (lnum <= curbuf->b_ml.ml_line_count) - { - /* existing line, replace it */ - if (u_savesub(lnum) == OK && ml_replace(lnum, line, TRUE) == OK) - { - changed_bytes(lnum, 0); - if (is_curbuf && lnum == curwin->w_cursor.lnum) - check_cursor_col(); - rettv->vval.v_number = 0; /* OK */ - } - } - else if (added > 0 || u_save(lnum - 1, lnum) == OK) - { - /* lnum is one past the last line, append the line */ - ++added; - if (ml_append(lnum - 1, line, (colnr_T)0, FALSE) == OK) - rettv->vval.v_number = 0; /* OK */ - } - - if (l == NULL) /* only one string argument */ - break; - ++lnum; - } - - if (added > 0) - appended_lines_mark(lcount, added); - - if (!is_curbuf) - { - curbuf = curbuf_save; - curwin = curwin_save; - } - } - - /* * "setbufline()" function */ static void --- 10327,10332 ---- *************** *** 10351,10358 **** else { lnum = get_tv_lnum_buf(&argvars[1], buf); ! ! set_buffer_lines(buf, lnum, &argvars[2], rettv); } } --- 10343,10349 ---- else { lnum = get_tv_lnum_buf(&argvars[1], buf); ! set_buffer_lines(buf, lnum, FALSE, &argvars[2], rettv); } } *************** *** 10512,10518 **** { linenr_T lnum = get_tv_lnum(&argvars[0]); ! set_buffer_lines(curbuf, lnum, &argvars[1], rettv); } static void set_qf_ll_list(win_T *wp, typval_T *list_arg, typval_T *action_arg, typval_T *what_arg, typval_T *rettv); --- 10503,10509 ---- { linenr_T lnum = get_tv_lnum(&argvars[0]); ! set_buffer_lines(curbuf, lnum, FALSE, &argvars[1], rettv); } static void set_qf_ll_list(win_T *wp, typval_T *list_arg, typval_T *action_arg, typval_T *what_arg, typval_T *rettv); *** ../vim-8.1.0036/src/testdir/test_bufline.vim 2017-11-06 21:26:52.000000000 +0100 --- src/testdir/test_bufline.vim 2018-06-06 20:58:47.740807362 +0200 *************** *** 1,4 **** ! " Tests for setbufline() and getbufline() source shared.vim --- 1,4 ---- ! " Tests for setbufline(), getbufline(), appendbufline() source shared.vim *************** *** 65,67 **** --- 65,92 ---- call delete('Xscript') call delete('Xtest') endfunc + + func Test_appendbufline() + new + let b = bufnr('%') + hide + call assert_equal(0, appendbufline(b, 0, ['foo', 'bar'])) + call assert_equal(['foo'], getbufline(b, 1)) + call assert_equal(['bar'], getbufline(b, 2)) + call assert_equal(['foo', 'bar'], getbufline(b, 1, 2)) + exe "bd!" b + call assert_equal([], getbufline(b, 1, 2)) + + split Xtest + call setline(1, ['a', 'b', 'c']) + let b = bufnr('%') + wincmd w + call assert_equal(1, appendbufline(b, 4, ['x'])) + call assert_equal(1, appendbufline(1234, 1, ['x'])) + call assert_equal(0, appendbufline(b, 3, ['d', 'e'])) + call assert_equal(['c'], getbufline(b, 3)) + call assert_equal(['d'], getbufline(b, 4)) + call assert_equal(['e'], getbufline(b, 5)) + call assert_equal([], getbufline(b, 6)) + exe "bwipe! " . b + endfunc *** ../vim-8.1.0036/src/testdir/test_edit.vim 2018-06-04 20:34:07.607373577 +0200 --- src/testdir/test_edit.vim 2018-06-06 20:58:47.740807362 +0200 *************** *** 527,533 **** " Tab in completion mode let path=expand("%:p:h") new ! call setline(1, [path."/", '']) call feedkeys("Arunt\\\\\", 'tnix') call assert_match('runtest\.vim', getline(1)) %d --- 527,533 ---- " Tab in completion mode let path=expand("%:p:h") new ! call setline(1, [path. "/", '']) call feedkeys("Arunt\\\\\", 'tnix') call assert_match('runtest\.vim', getline(1)) %d *** ../vim-8.1.0036/src/version.c 2018-06-06 18:02:31.402773772 +0200 --- src/version.c 2018-06-06 21:00:46.040701324 +0200 *************** *** 763,764 **** --- 763,766 ---- { /* Add new patch number below this line */ + /**/ + 37, /**/ -- How To Keep A Healthy Level Of Insanity: 16. Have your coworkers address you by your wrestling name, Rock Hard Kim. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///