vim-patch:8.0.1039: cannot change a line in not current buffer

Problem:    Cannot change a line in a buffer other than the current one.
Solution:   Add setbufline(). (Yasuhiro Matsumoto, Ozaki Kiichi, closes vim/vim#1953)
b31cf2bb0b
This commit is contained in:
Daniel Hahler
2019-06-17 14:42:51 +02:00
parent 4946751906
commit 9485061baa
4 changed files with 151 additions and 62 deletions

View File

@@ -2286,6 +2286,9 @@ searchpos({pattern} [, {flags} [, {stopline} [, {timeout}]]])
server2client({clientid}, {string}) server2client({clientid}, {string})
Number send reply string Number send reply string
serverlist() String get a list of available servers serverlist() String get a list of available servers
setbufline( {expr}, {lnum}, {line})
Number set line {lnum} to {line} in buffer
{expr}
setbufvar({expr}, {varname}, {val}) set {varname} in buffer {expr} to {val} setbufvar({expr}, {varname}, {val}) set {varname} in buffer {expr} to {val}
setcharsearch({dict}) Dict set character search from {dict} setcharsearch({dict}) Dict set character search from {dict}
setcmdpos({pos}) Number set cursor position in command-line setcmdpos({pos}) Number set cursor position in command-line
@@ -6997,6 +7000,19 @@ serverstop({address}) *serverstop()*
If |v:servername| is stopped it is set to the next available If |v:servername| is stopped it is set to the next available
address returned by |serverlist()|. address returned by |serverlist()|.
setbufline({expr}, {lnum}, {text}) *setbufline()*
Set line {lnum} to {text} in buffer {expr}. To insert
lines use |append()|.
For the use of {expr}, see |bufname()| above.
{lnum} is used like with |setline()|.
This works like |setline()| for the specified 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.
setbufvar({expr}, {varname}, {val}) *setbufvar()* setbufvar({expr}, {varname}, {val}) *setbufvar()*
Set option or local variable {varname} in buffer {expr} to Set option or local variable {varname} in buffer {expr} to
{val}. {val}.
@@ -7064,13 +7080,19 @@ setfperm({fname}, {mode}) *setfperm()* *chmod*
setline({lnum}, {text}) *setline()* setline({lnum}, {text}) *setline()*
Set line {lnum} of the current buffer to {text}. To insert Set line {lnum} of the current buffer to {text}. To insert
lines use |append()|. lines use |append()|. To set lines in another buffer use
|setbufline()|.
{lnum} is used like with |getline()|. {lnum} is used like with |getline()|.
When {lnum} is just below the last line the {text} will be When {lnum} is just below the last line the {text} will be
added as a new line. added as a new line.
If this succeeds, 0 is returned. If this fails (most likely If this succeeds, 0 is returned. If this fails (most likely
because {lnum} is invalid) 1 is returned. Example: > because {lnum} is invalid) 1 is returned.
Example: >
:call setline(5, strftime("%c")) :call setline(5, strftime("%c"))
< When {text} is a |List| then line {lnum} and following lines < When {text} is a |List| then line {lnum} and following lines
will be set to the items in the list. Example: > will be set to the items in the list. Example: >
:call setline(5, ['aaa', 'bbb', 'ccc']) :call setline(5, ['aaa', 'bbb', 'ccc'])

View File

@@ -14843,6 +14843,105 @@ static void f_serverstop(typval_T *argvars, typval_T *rettv, FunPtr fptr)
} }
} }
/// 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)
{
list_T *l = NULL;
listitem_T *li = NULL;
long added = 0;
linenr_T lcount;
buf_T *curbuf_save;
int is_curbuf = buf == curbuf;
if (buf == NULL || buf->b_ml.ml_mfp == NULL || lnum < 1) {
rettv->vval.v_number = 1; // FAIL
return;
}
curbuf_save = curbuf;
curbuf = buf;
lcount = curbuf->b_ml.ml_line_count;
const char *line = NULL;
if (lines->v_type == VAR_LIST) {
l = lines->vval.v_list;
li = tv_list_first(l);
} else {
line = tv_get_string_chk(lines);
}
// Default result is zero == OK.
for (;; ) {
if (lines->v_type == VAR_LIST) {
// List argument, get next string.
if (li == NULL) {
break;
}
line = tv_get_string_chk(TV_LIST_ITEM_TV(li));
li = TV_LIST_ITEM_NEXT(l, li);
}
rettv->vval.v_number = 1; // FAIL
if (line == NULL || lnum < 1 || 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, (char_u *)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, (char_u *)line, 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);
}
curbuf = curbuf_save;
}
/// "setbufline()" function
static void f_setbufline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
linenr_T lnum;
buf_T *buf;
buf = tv_get_buf(&argvars[0], false);
if (buf == NULL) {
rettv->vval.v_number = 1; // FAIL
} else {
lnum = tv_get_lnum_buf(&argvars[1], buf);
set_buffer_lines(buf, lnum, &argvars[2], rettv);
}
}
/* /*
* "setbufvar()" function * "setbufvar()" function
*/ */
@@ -14975,67 +15074,8 @@ static void f_setfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/ */
static void f_setline(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_setline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{ {
list_T *l = NULL;
listitem_T *li = NULL;
long added = 0;
linenr_T lcount = curbuf->b_ml.ml_line_count;
linenr_T lnum = tv_get_lnum(&argvars[0]); linenr_T lnum = tv_get_lnum(&argvars[0]);
const char *line = NULL; set_buffer_lines(curbuf, lnum, &argvars[1], rettv);
if (argvars[1].v_type == VAR_LIST) {
l = argvars[1].vval.v_list;
li = tv_list_first(l);
} else {
line = tv_get_string_chk(&argvars[1]);
}
// Default result is zero == OK.
for (;; ) {
if (argvars[1].v_type == VAR_LIST) {
// List argument, get next string.
if (li == NULL) {
break;
}
line = tv_get_string_chk(TV_LIST_ITEM_TV(li));
li = TV_LIST_ITEM_NEXT(l, li);
}
rettv->vval.v_number = 1; // FAIL
if (line == NULL || lnum < 1 || 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, (char_u *)line, true) == OK) {
changed_bytes(lnum, 0);
if (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, (char_u *)line, 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);
} }
/// Create quickfix/location list from VimL values /// Create quickfix/location list from VimL values

View File

@@ -263,6 +263,7 @@ return {
serverlist={}, serverlist={},
serverstart={args={0, 1}}, serverstart={args={0, 1}},
serverstop={args=1}, serverstop={args=1},
setbufline={args=3},
setbufvar={args=3}, setbufvar={args=3},
setcharsearch={args=1}, setcharsearch={args=1},
setcmdpos={args=1}, setcmdpos={args=1},

View File

@@ -0,0 +1,26 @@
" Tests for setbufline() and getbufline()
func Test_setbufline_getbufline()
new
let b = bufnr('%')
hide
call assert_equal(0, setbufline(b, 1, ['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, setbufline(b, 5, ['x']))
call assert_equal(1, setbufline(1234, 1, ['x']))
call assert_equal(0, setbufline(b, 4, ['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