diff --git a/runtime/doc/term.txt b/runtime/doc/term.txt index b46b399851..0bfe4eb0bf 100644 --- a/runtime/doc/term.txt +++ b/runtime/doc/term.txt @@ -1,4 +1,4 @@ -*term.txt* For Vim version 8.0. Last change: 2017 Jan 27 +*term.txt* For Vim version 8.0. Last change: 2017 Feb 02 VIM REFERENCE MANUAL by Bram Moolenaar @@ -95,7 +95,12 @@ terminal when entering "raw" mode and 't_BD' when leaving "raw" mode. The terminal is then expected to put 't_PS' before pasted text and 't_PE' after pasted text. This way Vim can separate text that is pasted from characters that are typed. The pasted text is handled like when the middle mouse button -is used. +is used, it is inserted literally and not interpreted as commands. + +When the cursor is in the first column, the pasted text will be inserted +before it. Otherwise the pasted text is appended after the cursor position. +This means one cannot paste after the first column. Unfortunately Vim does +not have a way to tell where the mouse pointer was. Note that in some situations Vim will not recognize the bracketed paste and you will get the raw text. In other situations Vim will only get the first diff --git a/src/Makefile b/src/Makefile index 669d3ab4a0..b3229e7f55 100644 --- a/src/Makefile +++ b/src/Makefile @@ -2144,8 +2144,9 @@ test_arglist \ test_fnameescape \ test_fnamemodify \ test_fold \ - test_glob2regpat \ + test_ga \ test_gf \ + test_glob2regpat \ test_gn \ test_goto \ test_gui \ diff --git a/src/ex_docmd.c b/src/ex_docmd.c index 6be5a786c0..c23cf5fe25 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -11825,7 +11825,7 @@ ses_arglist( s = buf; } } - if (fputs("argadd ", fd) < 0 + if (fputs("$argadd ", fd) < 0 || ses_put_fname(fd, s, flagp) == FAIL || put_eol(fd) == FAIL) { diff --git a/src/misc1.c b/src/misc1.c index ca4b5c3f83..c312c0df7e 100644 --- a/src/misc1.c +++ b/src/misc1.c @@ -3264,7 +3264,11 @@ change_warning( #endif msg_clr_eos(); (void)msg_end(); - if (msg_silent == 0 && !silent_mode) + if (msg_silent == 0 && !silent_mode +#ifdef FEAT_EVAL + && time_for_testing != 1 +#endif + ) { out_flush(); ui_delay(1000L, TRUE); /* give the user time to think about it */ diff --git a/src/normal.c b/src/normal.c index ac4be2aa57..1dd116d35f 100644 --- a/src/normal.c +++ b/src/normal.c @@ -9113,8 +9113,13 @@ nv_edit(cmdarg_T *cap) beginline(BL_WHITE|BL_FIX); break; + case K_PS: /* Bracketed paste works like "a"ppend, unless the + cursor is in the first column, then it inserts. */ + if (curwin->w_cursor.col == 0) + break; + /*FALLTHROUGH*/ + case 'a': /* "a"ppend is like "i"nsert on the next character. */ - case K_PS: /* bracketed paste works like "a"ppend */ #ifdef FEAT_VIRTUALEDIT /* increment coladd when in virtual space, increment the * column otherwise, also to append after an unprintable char */ diff --git a/src/ops.c b/src/ops.c index 0a489126c0..1c0276d8d7 100644 --- a/src/ops.c +++ b/src/ops.c @@ -2571,8 +2571,7 @@ op_insert(oparg_T *oap, long count1) } t1 = oap->start; - if (edit(NUL, FALSE, (linenr_T)count1)) - return; + (void)edit(NUL, FALSE, (linenr_T)count1); /* When a tab was inserted, and the characters in front of the tab * have been converted to a tab as well, the column of the cursor diff --git a/src/screen.c b/src/screen.c index c01a4ee9bc..8e2504af2f 100644 --- a/src/screen.c +++ b/src/screen.c @@ -2903,7 +2903,7 @@ win_line( int endrow, int nochange UNUSED) /* not updating for changed text */ { - int col; /* visual column on screen */ + int col = 0; /* visual column on screen */ unsigned off; /* offset in ScreenLines/ScreenAttrs */ int c = 0; /* init for GCC */ long vcol = 0; /* virtual column (for tabs) */ @@ -3429,7 +3429,11 @@ win_line( #else --ptr; #endif - n_skip = v - vcol; +#ifdef FEAT_MBYTE + /* character fits on the screen, don't need to skip it */ + if ((*mb_ptr2cells)(ptr) >= c && col == 0) +#endif + n_skip = v - vcol; } /* diff --git a/src/structs.h b/src/structs.h index 54cb1a5777..a7bf648844 100644 --- a/src/structs.h +++ b/src/structs.h @@ -1337,6 +1337,7 @@ typedef struct int uf_varargs; /* variable nr of arguments */ int uf_flags; int uf_calls; /* nr of active calls */ + int uf_cleared; /* func_clear() was already called */ garray_T uf_args; /* arguments */ garray_T uf_lines; /* function lines */ #ifdef FEAT_PROFILE diff --git a/src/testdir/runtest.vim b/src/testdir/runtest.vim index c2260c7b7f..33490b61bb 100644 --- a/src/testdir/runtest.vim +++ b/src/testdir/runtest.vim @@ -88,6 +88,14 @@ endfunc function RunTheTest(test) echo 'Executing ' . a:test + + " Avoid stopping at the "hit enter" prompt + set nomore + + " Avoid a three second wait when a message is about to be overwritten by the + " mode message. + set noshowmode + if exists("*SetUp") try call SetUp() @@ -158,12 +166,11 @@ let s:flaky = [ \ 'Test_communicate()', \ 'Test_nb_basic()', \ 'Test_pipe_through_sort_all()', - \ 'Test_pipe_through_sort_some()' + \ 'Test_pipe_through_sort_some()', \ 'Test_reltime()', \ ] " Locate Test_ functions and execute them. -set nomore redir @q silent function /^Test_ redir END diff --git a/src/testdir/test_alot.vim b/src/testdir/test_alot.vim index 2b163ae5ca..38b9f7a03c 100644 --- a/src/testdir/test_alot.vim +++ b/src/testdir/test_alot.vim @@ -19,6 +19,7 @@ source test_float_func.vim source test_fnamemodify.vim source test_functions.vim source test_glob2regpat.vim +source test_ga.vim source test_goto.vim source test_help_tagjump.vim source test_join.vim diff --git a/src/testdir/test_ga.vim b/src/testdir/test_ga.vim new file mode 100644 index 0000000000..f9357ddc87 --- /dev/null +++ b/src/testdir/test_ga.vim @@ -0,0 +1,37 @@ +" Test ga normal command, and :ascii Ex command. +func Do_ga(c) + call setline(1, a:c) + let l:a = execute("norm 1goga") + let l:b = execute("ascii") + call assert_equal(l:a, l:b) + return l:a +endfunc + +func Test_ga_command() + new + set display=uhex + call assert_equal("\nNUL", Do_ga('')) + call assert_equal("\n<<01>> 1, Hex 01, Octal 001", Do_ga("\x01")) + call assert_equal("\n<<09>> 9, Hex 09, Octal 011", Do_ga("\t")) + + set display= + call assert_equal("\nNUL", Do_ga('')) + call assert_equal("\n<^A> 1, Hex 01, Octal 001", Do_ga("\x01")) + call assert_equal("\n<^I> 9, Hex 09, Octal 011", Do_ga("\t")) + + call assert_equal("\n 101, Hex 65, Octal 145", Do_ga('e')) + + if !has('multi_byte') + return + endif + + " Test a few multi-bytes characters. + call assert_equal("\n<é> 233, Hex 00e9, Octal 351", Do_ga('é')) + call assert_equal("\n<ẻ> 7867, Hex 1ebb, Octal 17273", Do_ga('ẻ')) + + " Test with combining characters. + call assert_equal("\n 101, Hex 65, Octal 145 < ́> 769, Hex 0301, Octal 1401", Do_ga("e\u0301")) + call assert_equal("\n 101, Hex 65, Octal 145 < ́> 769, Hex 0301, Octal 1401 < ̱> 817, Hex 0331, Octal 1461", Do_ga("e\u0301\u0331")) + call assert_equal("\n 101, Hex 65, Octal 145 < ́> 769, Hex 0301, Octal 1401 < ̱> 817, Hex 0331, Octal 1461 < ̸> 824, Hex 0338, Octal 1470", Do_ga("e\u0301\u0331\u0338")) + bwipe! +endfunc diff --git a/src/testdir/test_mksession.vim b/src/testdir/test_mksession.vim index a51180cf0b..6ea3255dff 100644 --- a/src/testdir/test_mksession.vim +++ b/src/testdir/test_mksession.vim @@ -110,4 +110,16 @@ func Test_mksession_winheight() call delete('Xtest_mks.out') endfunc +func Test_mksession_arglist() + argdel * + next file1 file2 file3 file4 + mksession! Xtest_mks.out + source Xtest_mks.out + call assert_equal(['file1', 'file2', 'file3', 'file4'], argv()) + + call delete('Xtest_mks.out') + argdel * +endfunc + + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_normal.vim b/src/testdir/test_normal.vim index 98177851ab..3af79f8d04 100644 --- a/src/testdir/test_normal.vim +++ b/src/testdir/test_normal.vim @@ -2193,6 +2193,8 @@ func! Test_normal51_FileChangedRO() if !has("autocmd") return endif + " Don't sleep after the warning message. + call test_settime(1) call writefile(['foo'], 'Xreadonly.log') new Xreadonly.log setl ro @@ -2202,6 +2204,7 @@ func! Test_normal51_FileChangedRO() call assert_equal('Xreadonly.log', bufname('')) " cleanup + call test_settime(0) bw! call delete("Xreadonly.log") endfunc diff --git a/src/testdir/test_paste.vim b/src/testdir/test_paste.vim index 9961771421..f5deb7d6bc 100644 --- a/src/testdir/test_paste.vim +++ b/src/testdir/test_paste.vim @@ -8,18 +8,36 @@ set term=xterm func Test_paste_normal_mode() new + " In first column text is inserted call setline(1, ['a', 'b', 'c']) - 2 + call cursor(2, 1) call feedkeys("\[200~foo\bar\[201~", 'xt') - call assert_equal('bfoo', getline(2)) - call assert_equal('bar', getline(3)) + call assert_equal('foo', getline(2)) + call assert_equal('barb', getline(3)) call assert_equal('c', getline(4)) + " When repeating text is appended normal . call assert_equal('barfoo', getline(3)) - call assert_equal('bar', getline(4)) + call assert_equal('barb', getline(4)) call assert_equal('c', getline(5)) bwipe! + + " In second column text is appended + call setline(1, ['a', 'bbb', 'c']) + call cursor(2, 2) + call feedkeys("\[200~foo\bar\[201~", 'xt') + call assert_equal('bbfoo', getline(2)) + call assert_equal('barb', getline(3)) + call assert_equal('c', getline(4)) + + " In last column text is appended + call setline(1, ['a', 'bbb', 'c']) + call cursor(2, 3) + call feedkeys("\[200~foo\bar\[201~", 'xt') + call assert_equal('bbbfoo', getline(2)) + call assert_equal('bar', getline(3)) + call assert_equal('c', getline(4)) endfunc func Test_paste_insert_mode() diff --git a/src/testdir/test_stat.vim b/src/testdir/test_stat.vim index 89ca9ef379..6adc5a945f 100644 --- a/src/testdir/test_stat.vim +++ b/src/testdir/test_stat.vim @@ -1,24 +1,24 @@ " Tests for stat functions and checktime func Test_existent_file() - let fname='Xtest.tmp' + let fname = 'Xtest.tmp' - let ts=localtime() - sleep 1 - let fl=['Hello World!'] + let ts = localtime() + let fl = ['Hello World!'] call writefile(fl, fname) - let tf=getftime(fname) - sleep 1 - let te=localtime() + let tf = getftime(fname) + let te = localtime() call assert_true(ts <= tf && tf <= te) call assert_equal(strlen(fl[0] . "\n"), getfsize(fname)) call assert_equal('file', getftype(fname)) call assert_equal('rw-', getfperm(fname)[0:2]) + + call delete(fname) endfunc func Test_existent_directory() - let dname='.' + let dname = '.' call assert_equal(0, getfsize(dname)) call assert_equal('dir', getftype(dname)) @@ -26,22 +26,29 @@ func Test_existent_directory() endfunc func Test_checktime() - let fname='Xtest.tmp' + let fname = 'Xtest.tmp' - let fl=['Hello World!'] + let fl = ['Hello World!'] call writefile(fl, fname) set autoread exec 'e' fname - sleep 2 - let fl=readfile(fname) + " FAT has a granularity of 2 seconds, otherwise it's usually 1 second + if has('win32') + sleep 2 + else + sleep 1 + endif + let fl = readfile(fname) let fl[0] .= ' - checktime' call writefile(fl, fname) checktime call assert_equal(fl[0], getline(1)) + + call delete(fname) endfunc func Test_nonexistent_file() - let fname='Xtest.tmp' + let fname = 'Xtest.tmp' call delete(fname) call assert_equal(-1, getftime(fname)) @@ -55,7 +62,7 @@ func Test_win32_symlink_dir() " So we use an existing symlink for this test. if has('win32') " Check if 'C:\Users\All Users' is a symlink to a directory. - let res=system('dir C:\Users /a') + let res = system('dir C:\Users /a') if match(res, '\C *All Users') >= 0 " Get the filetype of the symlink. call assert_equal('dir', getftype('C:\Users\All Users')) diff --git a/src/testdir/test_visual.vim b/src/testdir/test_visual.vim index d84a14062b..8768395ba1 100644 --- a/src/testdir/test_visual.vim +++ b/src/testdir/test_visual.vim @@ -23,3 +23,16 @@ func Test_dotregister_paste() call assert_equal('hello world world', getline(1)) q! endfunc + +func Test_Visual_ctrl_o() + new + call setline(1, ['one', 'two', 'three']) + call cursor(1,2) + set noshowmode + set tw=0 + call feedkeys("\jjlIa\\:set tw=88\\", 'tx') + call assert_equal(['oane', 'tawo', 'tahree'], getline(1, 3)) + call assert_equal(88, &tw) + set tw& + bw! +endfu diff --git a/src/userfunc.c b/src/userfunc.c index 6a6cc06893..1bf028fa42 100644 --- a/src/userfunc.c +++ b/src/userfunc.c @@ -1075,12 +1075,17 @@ func_remove(ufunc_T *fp) } /* - * Free a function and remove it from the list of functions. + * Free all things that a function contains. Does not free the function + * itself, use func_free() for that. * When "force" is TRUE we are exiting. */ static void -func_free(ufunc_T *fp, int force) +func_clear(ufunc_T *fp, int force) { + if (fp->uf_cleared) + return; + fp->uf_cleared = TRUE; + /* clear this function */ ga_clear_strings(&(fp->uf_args)); ga_clear_strings(&(fp->uf_lines)); @@ -1089,16 +1094,35 @@ func_free(ufunc_T *fp, int force) vim_free(fp->uf_tml_total); vim_free(fp->uf_tml_self); #endif + funccal_unref(fp->uf_scoped, fp, force); +} + +/* + * Free a function and remove it from the list of functions. Does not free + * what a function contains, call func_clear() first. + */ + static void +func_free(ufunc_T *fp) +{ /* only remove it when not done already, otherwise we would remove a newer * version of the function */ if ((fp->uf_flags & (FC_DELETED | FC_REMOVED)) == 0) func_remove(fp); - funccal_unref(fp->uf_scoped, fp, force); - vim_free(fp); } +/* + * Free all things that a function contains and free the function itself. + * When "force" is TRUE we are exiting. + */ + static void +func_clear_free(ufunc_T *fp, int force) +{ + func_clear(fp, force); + func_free(fp); +} + /* * There are two kinds of function names: * 1. ordinary names, function defined with :function @@ -1120,10 +1144,40 @@ free_all_functions(void) hashitem_T *hi; ufunc_T *fp; long_u skipped = 0; - long_u todo; + long_u todo = 1; + long_u used; - /* Need to start all over every time, because func_free() may change the - * hash table. */ + /* First clear what the functions contain. Since this may lower the + * reference count of a function, it may also free a function and change + * the hash table. Restart if that happens. */ + while (todo > 0) + { + todo = func_hashtab.ht_used; + for (hi = func_hashtab.ht_array; todo > 0; ++hi) + if (!HASHITEM_EMPTY(hi)) + { + /* Only free functions that are not refcounted, those are + * supposed to be freed when no longer referenced. */ + fp = HI2UF(hi); + if (func_name_refcount(fp->uf_name)) + ++skipped; + else + { + used = func_hashtab.ht_used; + func_clear(fp, TRUE); + if (used != func_hashtab.ht_used) + { + skipped = 0; + break; + } + } + --todo; + } + } + + /* Now actually free the functions. Need to start all over every time, + * because func_free() may change the hash table. */ + skipped = 0; while (func_hashtab.ht_used > skipped) { todo = func_hashtab.ht_used; @@ -1138,7 +1192,7 @@ free_all_functions(void) ++skipped; else { - func_free(fp, TRUE); + func_free(fp); skipped = 0; break; } @@ -1356,7 +1410,7 @@ call_func( if (--fp->uf_calls <= 0 && fp->uf_refcount <= 0) /* Function was unreferenced while being used, free it * now. */ - func_free(fp, FALSE); + func_clear_free(fp, FALSE); if (did_save_redo) restoreRedobuff(); restore_search_patterns(); @@ -2756,7 +2810,7 @@ ex_delfunction(exarg_T *eap) fp->uf_flags |= FC_DELETED; } else - func_free(fp, FALSE); + func_clear_free(fp, FALSE); } } } @@ -2785,7 +2839,7 @@ func_unref(char_u *name) /* Only delete it when it's not being used. Otherwise it's done * when "uf_calls" becomes zero. */ if (fp->uf_calls == 0) - func_free(fp, FALSE); + func_clear_free(fp, FALSE); } } @@ -2801,7 +2855,7 @@ func_ptr_unref(ufunc_T *fp) /* Only delete it when it's not being used. Otherwise it's done * when "uf_calls" becomes zero. */ if (fp->uf_calls == 0) - func_free(fp, FALSE); + func_clear_free(fp, FALSE); } } @@ -3556,7 +3610,7 @@ get_funccal_args_var() { if (current_funccal == NULL) return NULL; - return ¤t_funccal->l_avars_var; + return &get_funccal()->l_avars_var; } /* diff --git a/src/version.c b/src/version.c index 3d3ba5fe1a..1f517b48d7 100644 --- a/src/version.c +++ b/src/version.c @@ -779,6 +779,28 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 297, +/**/ + 296, +/**/ + 295, +/**/ + 294, +/**/ + 293, +/**/ + 292, +/**/ + 291, +/**/ + 290, +/**/ + 289, +/**/ + 288, +/**/ + 287, /**/ 286, /**/