# HG changeset patch # User Christian Brabandt # Date 1528312505 -7200 # Node ID acb2dc112b06591a353fae11d868e6eba50fa23c # Parent 67370ee2bb4f9a271aa567dbfc802d14ad673a24 patch 8.1.0037: cannot easily append lines to another buffer commit https://github.com/vim/vim/commit/ca851593a660f08aba5c134f90c238d4a3e983e6 Author: Bram Moolenaar Date: Wed Jun 6 21:04:07 2018 +0200 patch 8.1.0037: cannot easily append lines to another buffer Problem: Cannot easily append lines to another buffer. Solution: Add appendbufline(). diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2560,6 +2560,21 @@ append({lnum}, {expr}) *append()* 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 diff --git a/src/evalfunc.c b/src/evalfunc.c --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -40,6 +40,7 @@ static void f_acos(typval_T *argvars, ty 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,6 +488,7 @@ static struct fst {"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,70 +1194,185 @@ f_and(typval_T *argvars, typval_T *rettv } /* + * 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) { - 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 */ + 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,22 +4393,6 @@ get_buffer_lines( } /* - * 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 @@ -10226,115 +10327,6 @@ f_serverlist(typval_T *argvars UNUSED, t } /* - * 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 @@ -10351,8 +10343,7 @@ f_setbufline(argvars, rettv) else { lnum = get_tv_lnum_buf(&argvars[1], buf); - - set_buffer_lines(buf, lnum, &argvars[2], rettv); + set_buffer_lines(buf, lnum, FALSE, &argvars[2], rettv); } } @@ -10512,7 +10503,7 @@ f_setline(typval_T *argvars, typval_T *r { linenr_T lnum = get_tv_lnum(&argvars[0]); - set_buffer_lines(curbuf, lnum, &argvars[1], rettv); + 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); diff --git a/src/testdir/test_bufline.vim b/src/testdir/test_bufline.vim --- a/src/testdir/test_bufline.vim +++ b/src/testdir/test_bufline.vim @@ -1,4 +1,4 @@ -" Tests for setbufline() and getbufline() +" Tests for setbufline(), getbufline(), appendbufline() source shared.vim @@ -65,3 +65,28 @@ func Test_setline_startup() 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 diff --git a/src/testdir/test_edit.vim b/src/testdir/test_edit.vim --- a/src/testdir/test_edit.vim +++ b/src/testdir/test_edit.vim @@ -527,7 +527,7 @@ func! Test_edit_CTRL_I() " Tab in completion mode let path=expand("%:p:h") new - call setline(1, [path."/", '']) + call setline(1, [path. "/", '']) call feedkeys("Arunt\\\\\", 'tnix') call assert_match('runtest\.vim', getline(1)) %d diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -762,6 +762,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 37, +/**/ 36, /**/ 35,