summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/eval.txt15
-rw-r--r--src/evalfunc.c323
-rw-r--r--src/testdir/test_bufline.vim27
-rw-r--r--src/testdir/test_edit.vim2
-rw-r--r--src/version.c2
5 files changed, 201 insertions, 168 deletions
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index 7d214c11aa..1c56bf3417 100644
--- 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
index 9441bd89f4..eb379a5cc1 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -40,6 +40,7 @@ static void f_acos(typval_T *argvars, typval_T *rettv);
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)
}
/*
- * "append(lnum, string/list)" function
+ * 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
-f_append(typval_T *argvars, typval_T *rettv)
+set_buffer_lines(
+ buf_T *buf,
+ linenr_T lnum_arg,
+ int append,
+ typval_T *lines,
+ typval_T *rettv)
{
- long lnum;
- char_u *line;
+ linenr_T lnum = lnum_arg + (append ? 1 : 0);
+ char_u *line = NULL;
list_T *l = NULL;
listitem_T *li = NULL;
- typval_T *tv;
long added = 0;
+ linenr_T append_lnum;
+ buf_T *curbuf_save = NULL;
+ win_T *curwin_save = NULL;
+ int is_curbuf = buf == curbuf;
- /* 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)
+ /* 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)
{
- u_sync_once = 1; /* notify that u_sync() was called */
- u_sync(TRUE);
+ rettv->vval.v_number = 1; /* FAIL */
+ return;
}
- lnum = get_tv_lnum(argvars);
- if (lnum >= 0
- && lnum <= curbuf->b_ml.ml_line_count
- && u_save(lnum, lnum + 1) == OK)
+ if (!is_curbuf)
{
- if (argvars[1].v_type == VAR_LIST)
- {
- l = argvars[1].vval.v_list;
- if (l == NULL)
- return;
- li = l->lv_first;
- }
- for (;;)
+ wininfo_T *wip;
+
+ curbuf_save = curbuf;
+ curwin_save = curwin;
+ curbuf = buf;
+ for (wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next)
{
- 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 */
+ if (wip->wi_win != NULL)
{
- rettv->vval.v_number = 1; /* Failed */
+ curwin = wip->wi_win;
break;
}
- ml_append(lnum + added, line, (colnr_T)0, FALSE);
- ++added;
- if (l == NULL)
+ }
+ }
+
+ 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;
}
- appended_lines_mark(lnum, added);
- if (curwin->w_cursor.lnum > lnum)
- curwin->w_cursor.lnum += added;
+ 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
- rettv->vval.v_number = 1; /* Failed */
+ {
+ 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, typval_T *rettv)
}
/*
- * 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 *rettv)
{
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
index b886e99506..b110c44eb1 100644
--- 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
index 3af8c4c880..ab2fe7c824 100644
--- 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\<c-x>\<c-f>\<tab>\<cr>\<esc>", 'tnix')
call assert_match('runtest\.vim', getline(1))
%d
diff --git a/src/version.c b/src/version.c
index 8f17fac840..100c01eeb9 100644
--- 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,