From 9485061baac53e5a6f07ddf05751b152078a1ab1 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Mon, 17 Jun 2019 14:42:51 +0200 Subject: 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) https://github.com/vim/vim/commit/b31cf2bb0be95d106bd8eef93cc07550591c1d0d --- runtime/doc/eval.txt | 26 ++++++- src/nvim/eval.c | 160 ++++++++++++++++++++++++-------------- src/nvim/eval.lua | 1 + src/nvim/testdir/test_bufline.vim | 26 +++++++ 4 files changed, 151 insertions(+), 62 deletions(-) create mode 100644 src/nvim/testdir/test_bufline.vim diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 4eaa72ee68..f84b525467 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2286,6 +2286,9 @@ searchpos({pattern} [, {flags} [, {stopline} [, {timeout}]]]) server2client({clientid}, {string}) Number send reply string 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} setcharsearch({dict}) Dict set character search from {dict} 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 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()* Set option or local variable {varname} in buffer {expr} to {val}. @@ -7064,13 +7080,19 @@ setfperm({fname}, {mode}) *setfperm()* *chmod* setline({lnum}, {text}) *setline()* 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()|. When {lnum} is just below the last line the {text} will be added as a new line. + 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")) + < When {text} is a |List| then line {lnum} and following lines will be set to the items in the list. Example: > :call setline(5, ['aaa', 'bbb', 'ccc']) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 15fc994957..d426fb2064 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -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 */ @@ -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) { - 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]); - const char *line = NULL; - 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); + set_buffer_lines(curbuf, lnum, &argvars[1], rettv); } /// Create quickfix/location list from VimL values diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index f36a7ea6c0..7cab0e7f80 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -263,6 +263,7 @@ return { serverlist={}, serverstart={args={0, 1}}, serverstop={args=1}, + setbufline={args=3}, setbufvar={args=3}, setcharsearch={args=1}, setcmdpos={args=1}, diff --git a/src/nvim/testdir/test_bufline.vim b/src/nvim/testdir/test_bufline.vim new file mode 100644 index 0000000000..7df36aa6a2 --- /dev/null +++ b/src/nvim/testdir/test_bufline.vim @@ -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 -- cgit From 8db93d0d83d834d736c1dddb51a4c94c89aae1be Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Tue, 18 Jun 2019 00:09:45 +0200 Subject: vim-patch:8.0.1053: setline() does not work on startup Problem: setline() does not work on startup. (Manuel Ortega) Solution: Do not check for ml_mfp to be set for the current buffer. (Christian Brabandt) https://github.com/vim/vim/commit/9d954207e2cc807b475bb04f8b59ef5bb3772d99 --- src/nvim/eval.c | 5 ++++- src/nvim/testdir/shared.vim | 14 ++++++++++---- src/nvim/testdir/test_bufline.vim | 15 +++++++++++++++ 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index d426fb2064..5ab1e0ebbe 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -14854,7 +14854,10 @@ static void set_buffer_lines(buf_T *buf, linenr_T lnum, typval_T *lines, buf_T *curbuf_save; int is_curbuf = buf == curbuf; - if (buf == NULL || buf->b_ml.ml_mfp == NULL || lnum < 1) { + // 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; } diff --git a/src/nvim/testdir/shared.vim b/src/nvim/testdir/shared.vim index ac34dec569..a5cf4def4f 100644 --- a/src/nvim/testdir/shared.vim +++ b/src/nvim/testdir/shared.vim @@ -190,12 +190,18 @@ func s:feedkeys(timer) endfunc " Get the command to run Vim, with -u NONE and --headless arguments. +" If there is an argument use it instead of "NONE". " Returns an empty string on error. -func GetVimCommand() +func GetVimCommand(...) + if a:0 == 0 + let name = 'NONE' + else + let name = a:1 + endif let cmd = v:progpath - let cmd = substitute(cmd, '-u \f\+', '-u NONE', '') - if cmd !~ '-u NONE' - let cmd = cmd . ' -u NONE' + let cmd = substitute(cmd, '-u \f\+', '-u ' . name, '') + if cmd !~ '-u '. name + let cmd = cmd . ' -u ' . name endif let cmd .= ' --headless -i NONE' let cmd = substitute(cmd, 'VIMRUNTIME=.*VIMRUNTIME;', '', '') diff --git a/src/nvim/testdir/test_bufline.vim b/src/nvim/testdir/test_bufline.vim index 7df36aa6a2..30e8ed8b42 100644 --- a/src/nvim/testdir/test_bufline.vim +++ b/src/nvim/testdir/test_bufline.vim @@ -1,5 +1,7 @@ " Tests for setbufline() and getbufline() +source shared.vim + func Test_setbufline_getbufline() new let b = bufnr('%') @@ -24,3 +26,16 @@ func Test_setbufline_getbufline() call assert_equal([], getbufline(b, 6)) exe "bwipe! " . b endfunc + +func Test_setline_startup() + let cmd = GetVimCommand('Xscript') + if cmd == '' + return + endif + call writefile(['call setline(1, "Hello")', 'w Xtest', 'q!'], 'Xscript') + call system(cmd) + call assert_equal(['Hello'], readfile('Xtest')) + + call delete('Xscript') + call delete('Xtest') +endfunc -- cgit From d43cf02186d10d96e325704e0bb146e3fd91a11d Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Tue, 18 Jun 2019 00:21:04 +0200 Subject: vim-patch:8.0.1055: bufline test hangs on MS-Windows Problem: Bufline test hangs on MS-Windows. Solution: Avoid message for writing file. Source shared.vim when running test individually. https://github.com/vim/vim/commit/11aa62f8f949bb590b4d7792a334885fba5e4137 --- src/nvim/testdir/test_bufline.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nvim/testdir/test_bufline.vim b/src/nvim/testdir/test_bufline.vim index 30e8ed8b42..cc5c10e985 100644 --- a/src/nvim/testdir/test_bufline.vim +++ b/src/nvim/testdir/test_bufline.vim @@ -32,7 +32,7 @@ func Test_setline_startup() if cmd == '' return endif - call writefile(['call setline(1, "Hello")', 'w Xtest', 'q!'], 'Xscript') + call writefile(['call setline(1, "Hello")', 'silent w Xtest', 'q!'], 'Xscript') call system(cmd) call assert_equal(['Hello'], readfile('Xtest')) -- cgit From d0e1417254099eb8f8bee066e84d5a49246e737f Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Tue, 18 Jun 2019 00:48:09 +0200 Subject: vim-patch:8.0.1274: setbufline() fails when using folding Problem: setbufline() fails when using folding. Solution: Set "curwin" if needed. (Ozaki Kiichi, closes vim/vim#2293) https://github.com/vim/vim/commit/0c4dc88a637a5027209aa00226996af84e248636 --- src/nvim/eval.c | 23 +++++++++++++++++++---- src/nvim/testdir/test_bufline.vim | 26 ++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 5ab1e0ebbe..1d34af2230 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -14851,7 +14851,8 @@ static void set_buffer_lines(buf_T *buf, linenr_T lnum, typval_T *lines, listitem_T *li = NULL; long added = 0; linenr_T lcount; - buf_T *curbuf_save; + 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 @@ -14862,8 +14863,19 @@ static void set_buffer_lines(buf_T *buf, linenr_T lnum, typval_T *lines, return; } - curbuf_save = curbuf; - curbuf = buf; + 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; @@ -14926,7 +14938,10 @@ static void set_buffer_lines(buf_T *buf, linenr_T lnum, typval_T *lines, appended_lines_mark(lcount, added); } - curbuf = curbuf_save; + if (!is_curbuf) { + curbuf = curbuf_save; + curwin = curwin_save; + } } /// "setbufline()" function diff --git a/src/nvim/testdir/test_bufline.vim b/src/nvim/testdir/test_bufline.vim index cc5c10e985..b886e99506 100644 --- a/src/nvim/testdir/test_bufline.vim +++ b/src/nvim/testdir/test_bufline.vim @@ -27,6 +27,32 @@ func Test_setbufline_getbufline() exe "bwipe! " . b endfunc +func Test_setbufline_getbufline_fold() + split Xtest + setlocal foldmethod=expr foldexpr=0 + let b = bufnr('%') + new + 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 "bwipe!" b + bwipe! +endfunc + +func Test_setbufline_getbufline_fold_tab() + split Xtest + setlocal foldmethod=expr foldexpr=0 + let b = bufnr('%') + tab new + 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 "bwipe!" b + bwipe! +endfunc + func Test_setline_startup() let cmd = GetVimCommand('Xscript') if cmd == '' -- cgit