From a6c27ee6db2c328e0ab0e6d143e2a295a0bb9c9a Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 15 Oct 2016 14:56:30 +0200 Subject: [PATCH 1/7] patch 8.0.0033 Problem: Cannot use overlapping positions with matchaddpos(). Solution: Check end of match. (Ozaki Kiichi) Add a test (Hirohito Higashi) --- src/screen.c | 14 ++++++++------ src/testdir/test_match.vim | 10 ++++++++++ src/version.c | 2 ++ 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/screen.c b/src/screen.c index 5ebca09804..0889db91d6 100644 --- a/src/screen.c +++ b/src/screen.c @@ -7786,21 +7786,23 @@ next_search_hl_pos( shl->lnum = 0; for (i = posmatch->cur; i < MAXPOSMATCH; i++) { - if (posmatch->pos[i].lnum == 0) + llpos_T *pos = &posmatch->pos[i]; + + if (pos->lnum == 0) break; - if (posmatch->pos[i].col < mincol) + if (pos->col + pos->len - 1 <= mincol) continue; - if (posmatch->pos[i].lnum == lnum) + if (pos->lnum == lnum) { if (shl->lnum == lnum) { /* partially sort positions by column numbers * on the same line */ - if (posmatch->pos[i].col < posmatch->pos[bot].col) + if (pos->col < posmatch->pos[bot].col) { - llpos_T tmp = posmatch->pos[i]; + llpos_T tmp = *pos; - posmatch->pos[i] = posmatch->pos[bot]; + *pos = posmatch->pos[bot]; posmatch->pos[bot] = tmp; } } diff --git a/src/testdir/test_match.vim b/src/testdir/test_match.vim index 9ac1db157f..3b20d5d439 100644 --- a/src/testdir/test_match.vim +++ b/src/testdir/test_match.vim @@ -181,6 +181,16 @@ func Test_matchaddpos() redraw! call assert_equal(screenattr(2,2), screenattr(1,6)) + " Check overlapping pos + call clearmatches() + call setline(1, ['1234567890', 'NH']) + call matchaddpos('Error', [[1,1,5], [1,3,5], [2,2]]) + redraw! + call assert_notequal(screenattr(2,2), 0) + call assert_equal(screenattr(2,2), screenattr(1,5)) + call assert_equal(screenattr(2,2), screenattr(1,7)) + call assert_notequal(screenattr(2,2), screenattr(1,8)) + nohl syntax off set hlsearch& diff --git a/src/version.c b/src/version.c index 6fee4abf73..b2066e4e84 100644 --- a/src/version.c +++ b/src/version.c @@ -764,6 +764,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 33, /**/ 32, /**/ From 9e507ca8a3e1535e62de4bd86374b0fcd18ef5b8 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 15 Oct 2016 15:39:39 +0200 Subject: [PATCH 2/7] patch 8.0.0034 Problem: No completion for ":messages". Solution: Complete "clear" argument. (Hirohito Higashi) --- runtime/doc/eval.txt | 2 ++ runtime/doc/map.txt | 1 + src/ex_docmd.c | 18 ++++++++++++++++++ src/ex_getln.c | 1 + src/proto/ex_docmd.pro | 1 + src/testdir/test_cmdline.vim | 5 +++++ src/version.c | 2 ++ src/vim.h | 1 + 8 files changed, 31 insertions(+) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index d4a005ae36..88599ddecb 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -4287,7 +4287,9 @@ getcompletion({pat}, {type} [, {filtered}]) *getcompletion()* locale locale names (as output of locale -a) mapping mapping name menu menus + messages |:messages| suboptions option options + packadd optional package |pack-add| names shellcmd Shell command sign |:sign| suboptions syntax syntax file names |'syntax'| diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt index 7989d64567..25ed827a80 100644 --- a/runtime/doc/map.txt +++ b/runtime/doc/map.txt @@ -1280,6 +1280,7 @@ completion can be enabled: -complete=locale locale names (as output of locale -a) -complete=mapping mapping name -complete=menu menus + -complete=messages |:messages| suboptions -complete=option options -complete=packadd optional package |pack-add| names -complete=shellcmd Shell command diff --git a/src/ex_docmd.c b/src/ex_docmd.c index ac7beb7c5b..6802b8b3d7 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -4281,6 +4281,11 @@ set_one_cmd_context( xp->xp_pattern = arg; break; + case CMD_messages: + xp->xp_context = EXPAND_MESSAGES; + xp->xp_pattern = arg; + break; + #if defined(FEAT_CMDHIST) case CMD_history: xp->xp_context = EXPAND_HISTORY; @@ -5893,6 +5898,7 @@ static struct #endif {EXPAND_MAPPINGS, "mapping"}, {EXPAND_MENUS, "menu"}, + {EXPAND_MESSAGES, "messages"}, {EXPAND_OWNSYNTAX, "syntax"}, #if defined(FEAT_PROFILE) {EXPAND_SYNTIME, "syntime"}, @@ -11901,6 +11907,18 @@ get_behave_arg(expand_T *xp UNUSED, int idx) return (char_u *)"xterm"; return NULL; } + +/* + * Function given to ExpandGeneric() to obtain the possible arguments of the + * ":messages {clear}" command. + */ + char_u * +get_messages_arg(expand_T *xp UNUSED, int idx) +{ + if (idx == 0) + return (char_u *)"clear"; + return NULL; +} #endif #ifdef FEAT_AUTOCMD diff --git a/src/ex_getln.c b/src/ex_getln.c index 25a12bed3d..5d098cca3f 100644 --- a/src/ex_getln.c +++ b/src/ex_getln.c @@ -4832,6 +4832,7 @@ ExpandFromContext( { {EXPAND_COMMANDS, get_command_name, FALSE, TRUE}, {EXPAND_BEHAVE, get_behave_arg, TRUE, TRUE}, + {EXPAND_MESSAGES, get_messages_arg, TRUE, TRUE}, #ifdef FEAT_CMDHIST {EXPAND_HISTORY, get_history_arg, TRUE, TRUE}, #endif diff --git a/src/proto/ex_docmd.pro b/src/proto/ex_docmd.pro index b92ce80d13..11a3f71aca 100644 --- a/src/proto/ex_docmd.pro +++ b/src/proto/ex_docmd.pro @@ -61,4 +61,5 @@ int put_eol(FILE *fd); int put_line(FILE *fd, char *s); void dialog_msg(char_u *buff, char *format, char_u *fname); char_u *get_behave_arg(expand_T *xp, int idx); +char_u *get_messages_arg(expand_T *xp, int idx); /* vim: set ft=c : */ diff --git a/src/testdir/test_cmdline.vim b/src/testdir/test_cmdline.vim index 4bacf5721d..3718087eb1 100644 --- a/src/testdir/test_cmdline.vim +++ b/src/testdir/test_cmdline.vim @@ -129,6 +129,11 @@ func Test_getcompletion() let l = getcompletion('dark', 'highlight') call assert_equal([], l) + let l = getcompletion('', 'messages') + call assert_true(index(l, 'clear') >= 0) + let l = getcompletion('not', 'messages') + call assert_equal([], l) + if has('cscope') let l = getcompletion('', 'cscope') let cmds = ['add', 'find', 'help', 'kill', 'reset', 'show'] diff --git a/src/version.c b/src/version.c index b2066e4e84..e803bae453 100644 --- a/src/version.c +++ b/src/version.c @@ -764,6 +764,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 34, /**/ 33, /**/ diff --git a/src/vim.h b/src/vim.h index a787575b8d..0ac296e260 100644 --- a/src/vim.h +++ b/src/vim.h @@ -793,6 +793,7 @@ extern char *(*dyn_libintl_textdomain)(const char *domainname); #define EXPAND_SYNTIME 43 #define EXPAND_USER_ADDR_TYPE 44 #define EXPAND_PACKADD 45 +#define EXPAND_MESSAGES 46 /* Values for exmode_active (0 is no exmode) */ #define EXMODE_NORMAL 1 From 472e85970ee3a80abd824bef510df12e9cfe9e96 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 15 Oct 2016 17:06:47 +0200 Subject: [PATCH 3/7] patch 8.0.0035 Problem: Order of matches for 'omnifunc' is messed up. (Danny Su) Solution: Do not set compl_curr_match when called from complete_check(). (closes #1168) --- src/Makefile | 2 +- src/edit.c | 38 +++++++------ src/evalfunc.c | 2 +- src/proto/edit.pro | 2 +- src/search.c | 2 +- src/spell.c | 2 +- src/tag.c | 2 +- src/testdir/Make_all.mak | 1 - src/testdir/test76.in | 46 --------------- src/testdir/test76.ok | 4 -- src/testdir/test_popup.vim | 111 +++++++++++++++++++++++++++++++++++++ src/version.c | 2 + 12 files changed, 141 insertions(+), 73 deletions(-) delete mode 100644 src/testdir/test76.in delete mode 100644 src/testdir/test76.ok diff --git a/src/Makefile b/src/Makefile index 1589fbad72..fd07946baa 100644 --- a/src/Makefile +++ b/src/Makefile @@ -2047,7 +2047,7 @@ test1 \ test40 test41 test42 test43 test44 test45 test48 test49 \ test50 test51 test52 test53 test54 test55 test56 test57 test58 test59 \ test60 test64 test65 test66 test67 test68 test69 \ - test70 test72 test73 test74 test75 test76 test77 test78 test79 \ + test70 test72 test73 test74 test75 test77 test78 test79 \ test80 test82 test83 test84 test85 test86 test87 test88 test89 \ test90 test91 test92 test93 test94 test95 test97 test98 test99 \ test100 test101 test103 test104 test107 test108: diff --git a/src/edit.c b/src/edit.c index a87c155c7c..d763b53183 100644 --- a/src/edit.c +++ b/src/edit.c @@ -179,8 +179,8 @@ static void ins_compl_add_dict(dict_T *dict); #endif static int ins_compl_get_exp(pos_T *ini); static void ins_compl_delete(void); -static void ins_compl_insert(void); -static int ins_compl_next(int allow_get_expansion, int count, int insert_match); +static void ins_compl_insert(int in_compl_func); +static int ins_compl_next(int allow_get_expansion, int count, int insert_match, int in_compl_func); static int ins_compl_key2dir(int c); static int ins_compl_pum_key(int c); static int ins_compl_key2count(int c); @@ -861,7 +861,7 @@ edit( && (c == CAR || c == K_KENTER || c == NL))) { ins_compl_delete(); - ins_compl_insert(); + ins_compl_insert(FALSE); } } } @@ -3297,7 +3297,7 @@ ins_compl_files( break; } line_breakcheck(); - ins_compl_check_keys(50); + ins_compl_check_keys(50, FALSE); } fclose(fp); } @@ -4036,8 +4036,6 @@ ins_compl_next_buf(buf_T *buf, int flag) } #ifdef FEAT_COMPL_FUNC -static void expand_by_function(int type, char_u *base); - /* * Execute user defined complete function 'completefunc' or 'omnifunc', and * get matches in "matches". @@ -4596,7 +4594,7 @@ ins_compl_get_exp(pos_T *ini) break; /* Fill the popup menu as soon as possible. */ if (type != -1) - ins_compl_check_keys(0); + ins_compl_check_keys(0, FALSE); if ((ctrl_x_mode != 0 && !CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode)) || compl_interrupted) @@ -4653,9 +4651,12 @@ ins_compl_delete(void) set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc()); } -/* Insert the new text being completed. */ +/* + * Insert the new text being completed. + * "in_compl_func" is TRUE when called from complete_check(). + */ static void -ins_compl_insert(void) +ins_compl_insert(int in_compl_func) { dict_T *dict; @@ -4682,7 +4683,8 @@ ins_compl_insert(void) EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_INFO])); } set_vim_var_dict(VV_COMPLETED_ITEM, dict); - compl_curr_match = compl_shown_match; + if (!in_compl_func) + compl_curr_match = compl_shown_match; } /* @@ -4706,7 +4708,8 @@ ins_compl_next( int allow_get_expansion, int count, /* repeat completion this many times; should be at least 1 */ - int insert_match) /* Insert the newly selected match */ + int insert_match, /* Insert the newly selected match */ + int in_compl_func) /* called from complete_check() */ { int num_matches = -1; int i; @@ -4856,7 +4859,7 @@ ins_compl_next( else if (insert_match) { if (!compl_get_longest || compl_used_match) - ins_compl_insert(); + ins_compl_insert(in_compl_func); else ins_bytes(compl_leader + ins_compl_len()); } @@ -4921,9 +4924,11 @@ ins_compl_next( * mode. Also, when compl_pending is not zero, show a completion as soon as * possible. -- webb * "frequency" specifies out of how many calls we actually check. + * "in_compl_func" is TRUE when called from complete_check(), don't set + * compl_curr_match. */ void -ins_compl_check_keys(int frequency) +ins_compl_check_keys(int frequency, int in_compl_func) { static int count = 0; @@ -4949,7 +4954,7 @@ ins_compl_check_keys(int frequency) c = safe_vgetc(); /* Eat the character */ compl_shows_dir = ins_compl_key2dir(c); (void)ins_compl_next(FALSE, ins_compl_key2count(c), - c != K_UP && c != K_DOWN); + c != K_UP && c != K_DOWN, in_compl_func); } else { @@ -4972,7 +4977,7 @@ ins_compl_check_keys(int frequency) int todo = compl_pending > 0 ? compl_pending : -compl_pending; compl_pending = 0; - (void)ins_compl_next(FALSE, todo, TRUE); + (void)ins_compl_next(FALSE, todo, TRUE, in_compl_func); } } @@ -5490,7 +5495,8 @@ ins_complete(int c, int enable_pum) * Find next match (and following matches). */ save_w_wrow = curwin->w_wrow; - n = ins_compl_next(TRUE, ins_compl_key2count(c), ins_compl_use_match(c)); + n = ins_compl_next(TRUE, ins_compl_key2count(c), + ins_compl_use_match(c), FALSE); /* may undisplay the popup menu */ ins_compl_upd_pum(); diff --git a/src/evalfunc.c b/src/evalfunc.c index 9fa8f1c9ee..f643329c95 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -2175,7 +2175,7 @@ f_complete_check(typval_T *argvars UNUSED, typval_T *rettv) int saved = RedrawingDisabled; RedrawingDisabled = 0; - ins_compl_check_keys(0); + ins_compl_check_keys(0, TRUE); rettv->vval.v_number = compl_interrupted; RedrawingDisabled = saved; } diff --git a/src/proto/edit.pro b/src/proto/edit.pro index e32b5ba25a..7a399c0e6e 100644 --- a/src/proto/edit.pro +++ b/src/proto/edit.pro @@ -15,7 +15,7 @@ char_u *find_word_start(char_u *ptr); char_u *find_word_end(char_u *ptr); int ins_compl_active(void); int ins_compl_add_tv(typval_T *tv, int dir); -void ins_compl_check_keys(int frequency); +void ins_compl_check_keys(int frequency, int in_compl_func); int get_literal(void); void insertchar(int c, int flags, int second_indent); void auto_format(int trailblank, int prev_line); diff --git a/src/search.c b/src/search.c index cb5caeb6da..cedcad9eb1 100644 --- a/src/search.c +++ b/src/search.c @@ -5429,7 +5429,7 @@ exit_matched: line_breakcheck(); #ifdef FEAT_INS_EXPAND if (action == ACTION_EXPAND) - ins_compl_check_keys(30); + ins_compl_check_keys(30, FALSE); if (got_int || compl_interrupted) #else if (got_int) diff --git a/src/spell.c b/src/spell.c index 69ac6dfbc8..eb81ad27bf 100644 --- a/src/spell.c +++ b/src/spell.c @@ -8694,7 +8694,7 @@ spell_dump_compl( /* Done all bytes at this node, go up one level. */ --depth; line_breakcheck(); - ins_compl_check_keys(50); + ins_compl_check_keys(50, FALSE); } else { diff --git a/src/tag.c b/src/tag.c index d679686a5e..14fdcf8b09 100644 --- a/src/tag.c +++ b/src/tag.c @@ -1587,7 +1587,7 @@ find_tags( fast_breakcheck(); #ifdef FEAT_INS_EXPAND if ((flags & TAG_INS_COMP)) /* Double brackets for gcc */ - ins_compl_check_keys(30); + ins_compl_check_keys(30, FALSE); if (got_int || compl_interrupted) #else if (got_int) diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak index a997580480..a8ea54318e 100644 --- a/src/testdir/Make_all.mak +++ b/src/testdir/Make_all.mak @@ -55,7 +55,6 @@ SCRIPTS_ALL = \ test70.out \ test73.out \ test75.out \ - test76.out \ test77.out \ test79.out \ test80.out \ diff --git a/src/testdir/test76.in b/src/testdir/test76.in deleted file mode 100644 index db7ebe2169..0000000000 --- a/src/testdir/test76.in +++ /dev/null @@ -1,46 +0,0 @@ -Tests for completefunc/omnifunc. vim: set ft=vim : - -STARTTEST -:"Test that nothing happens if the 'completefunc' opens -:"a new window (no completion, no crash) -:so small.vim -:function! DummyCompleteOne(findstart, base) -: if a:findstart -: return 0 -: else -: wincmd n -: return ['onedef', 'oneDEF'] -: endif -:endfunction -:setlocal completefunc=DummyCompleteOne -/^one -A:q! -:function! DummyCompleteTwo(findstart, base) -: if a:findstart -: wincmd n -: return 0 -: else -: return ['twodef', 'twoDEF'] -: endif -:endfunction -:setlocal completefunc=DummyCompleteTwo -/^two -A:q! -:"Test that 'completefunc' works when it's OK. -:function! DummyCompleteThree(findstart, base) -: if a:findstart -: return 0 -: else -: return ['threedef', 'threeDEF'] -: endif -:endfunction -:setlocal completefunc=DummyCompleteThree -/^three -A:/^+++/,/^three/w! test.out -:qa! -ENDTEST - -+++ -one -two -three diff --git a/src/testdir/test76.ok b/src/testdir/test76.ok deleted file mode 100644 index 2a70acbade..0000000000 --- a/src/testdir/test76.ok +++ /dev/null @@ -1,4 +0,0 @@ -+++ - -two -threeDEF diff --git a/src/testdir/test_popup.vim b/src/testdir/test_popup.vim index 10eaf3a632..7cb0e10aa1 100644 --- a/src/testdir/test_popup.vim +++ b/src/testdir/test_popup.vim @@ -289,4 +289,115 @@ func Test_compl_vim_cmds_after_register_expr() bwipe! endfunc +func DummyCompleteOne(findstart, base) + if a:findstart + return 0 + else + wincmd n + return ['onedef', 'oneDEF'] + endif +endfunc + +" Test that nothing happens if the 'completefunc' opens +" a new window (no completion, no crash) +func Test_completefunc_opens_new_window_one() + new + let winid = win_getid() + setlocal completefunc=DummyCompleteOne + call setline(1, 'one') + /^one + call assert_fails('call feedkeys("A\\\\", "x")', 'E839:') + call assert_notequal(winid, win_getid()) + q! + call assert_equal(winid, win_getid()) + call assert_equal('', getline(1)) + q! +endfunc + +" Test that nothing happens if the 'completefunc' opens +" a new window (no completion, no crash) +func DummyCompleteTwo(findstart, base) + if a:findstart + wincmd n + return 0 + else + return ['twodef', 'twoDEF'] + endif +endfunction + +" Test that nothing happens if the 'completefunc' opens +" a new window (no completion, no crash) +func Test_completefunc_opens_new_window_two() + new + let winid = win_getid() + setlocal completefunc=DummyCompleteTwo + call setline(1, 'two') + /^two + call assert_fails('call feedkeys("A\\\\", "x")', 'E764:') + call assert_notequal(winid, win_getid()) + q! + call assert_equal(winid, win_getid()) + call assert_equal('two', getline(1)) + q! +endfunc + +func DummyCompleteThree(findstart, base) + if a:findstart + return 0 + else + return ['threedef', 'threeDEF'] + endif +endfunc + +:"Test that 'completefunc' works when it's OK. +func Test_completefunc_works() + new + let winid = win_getid() + setlocal completefunc=DummyCompleteThree + call setline(1, 'three') + /^three + call feedkeys("A\\\\", "x") + call assert_equal(winid, win_getid()) + call assert_equal('threeDEF', getline(1)) + q! +endfunc + +func DummyCompleteFour(findstart, base) + if a:findstart + return 0 + else + call complete_add('four1') + call complete_add('four2') + call complete_check() + call complete_add('four3') + call complete_add('four4') + call complete_check() + call complete_add('four5') + call complete_add('four6') + return [] + endif +endfunc + +:"Test that 'completefunc' works when it's OK. +func Test_omnifunc_with_check() + new + setlocal omnifunc=DummyCompleteFour + call setline(1, 'four') + /^four + call feedkeys("A\\\\", "x") + call assert_equal('four2', getline(1)) + + call setline(1, 'four') + /^four + call feedkeys("A\\\\\", "x") + call assert_equal('four3', getline(1)) + + call setline(1, 'four') + /^four + call feedkeys("A\\\\\\\", "x") + call assert_equal('four5', getline(1)) + + q! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index e803bae453..513223e450 100644 --- a/src/version.c +++ b/src/version.c @@ -764,6 +764,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 35, /**/ 34, /**/ From 97792de2762cc79cc365a8a0b858f27753179577 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 15 Oct 2016 18:36:49 +0200 Subject: [PATCH 4/7] patch 8.0.0036 Problem: Detecting that a job has finished may take a while. Solution: Check for a finished job more often (Ozaki Kiichi) --- src/channel.c | 108 +++++++++++++++++++---------------- src/os_unix.c | 57 ++++++++++++++++-- src/os_win32.c | 42 +++++++++++++- src/proto/os_unix.pro | 1 + src/proto/os_win32.pro | 1 + src/testdir/test_channel.vim | 18 ++++++ src/version.c | 2 + 7 files changed, 173 insertions(+), 56 deletions(-) diff --git a/src/channel.c b/src/channel.c index 2d68287a45..0bd23785e7 100644 --- a/src/channel.c +++ b/src/channel.c @@ -4428,6 +4428,39 @@ job_free(job_T *job) } } + static void +job_cleanup(job_T *job) +{ + if (job->jv_status != JOB_ENDED) + return; + + if (job->jv_exit_cb != NULL) + { + typval_T argv[3]; + typval_T rettv; + int dummy; + + /* invoke the exit callback; make sure the refcount is > 0 */ + ++job->jv_refcount; + argv[0].v_type = VAR_JOB; + argv[0].vval.v_job = job; + argv[1].v_type = VAR_NUMBER; + argv[1].vval.v_number = job->jv_exitval; + call_func(job->jv_exit_cb, (int)STRLEN(job->jv_exit_cb), + &rettv, 2, argv, NULL, 0L, 0L, &dummy, TRUE, + job->jv_exit_partial, NULL); + clear_tv(&rettv); + --job->jv_refcount; + channel_need_redraw = TRUE; + } + if (job->jv_refcount == 0) + { + /* The job was already unreferenced, now that it ended it can be + * freed. Careful: caller must not use "job" after this! */ + job_free(job); + } +} + #if defined(EXITFREE) || defined(PROTO) void job_free_all(void) @@ -4445,10 +4478,15 @@ job_free_all(void) static int job_still_useful(job_T *job) { - return job->jv_status == JOB_STARTED - && (job->jv_stoponexit != NULL || job->jv_exit_cb != NULL - || (job->jv_channel != NULL - && channel_still_useful(job->jv_channel))); + return (job->jv_stoponexit != NULL || job->jv_exit_cb != NULL + || (job->jv_channel != NULL + && channel_still_useful(job->jv_channel))); +} + + static int +job_still_alive(job_T *job) +{ + return (job->jv_status == JOB_STARTED) && job_still_useful(job); } /* @@ -4462,7 +4500,7 @@ set_ref_in_job(int copyID) typval_T tv; for (job = first_job; job != NULL; job = job->jv_next) - if (job_still_useful(job)) + if (job_still_alive(job)) { tv.v_type = VAR_JOB; tv.vval.v_job = job; @@ -4478,7 +4516,7 @@ job_unref(job_T *job) { /* Do not free the job when it has not ended yet and there is a * "stoponexit" flag or an exit callback. */ - if (!job_still_useful(job)) + if (!job_still_alive(job)) { job_free(job); } @@ -4503,7 +4541,7 @@ free_unused_jobs_contents(int copyID, int mask) for (job = first_job; job != NULL; job = job->jv_next) if ((job->jv_copyID & mask) != (copyID & mask) - && !job_still_useful(job)) + && !job_still_alive(job)) { /* Free the channel and ordinary items it contains, but don't * recurse into Lists, Dictionaries etc. */ @@ -4523,7 +4561,7 @@ free_unused_jobs(int copyID, int mask) { job_next = job->jv_next; if ((job->jv_copyID & mask) != (copyID & mask) - && !job_still_useful(job)) + && !job_still_alive(job)) { /* Free the job struct itself. */ job_free_job(job); @@ -4614,34 +4652,31 @@ has_pending_job(void) job_T *job; for (job = first_job; job != NULL; job = job->jv_next) - if (job->jv_status == JOB_STARTED && job_still_useful(job)) + if (job_still_alive(job)) return TRUE; return FALSE; } +#define MAX_CHECK_ENDED 8 + /* * Called once in a while: check if any jobs that seem useful have ended. */ void job_check_ended(void) { - static time_t last_check = 0; - time_t now; - job_T *job; - job_T *next; + int i; - /* Only do this once in 10 seconds. */ - now = time(NULL); - if (last_check + 10 < now) + for (i = 0; i < MAX_CHECK_ENDED; ++i) { - last_check = now; - for (job = first_job; job != NULL; job = next) - { - next = job->jv_next; - if (job->jv_status == JOB_STARTED && job_still_useful(job)) - job_status(job); /* may free "job" */ - } + job_T *job = mch_detect_ended_job(first_job); + + if (job == NULL) + break; + if (job_still_useful(job)) + job_cleanup(job); /* may free "job" */ } + if (channel_need_redraw) { channel_need_redraw = FALSE; @@ -4862,32 +4897,7 @@ job_status(job_T *job) { result = mch_job_status(job); if (job->jv_status == JOB_ENDED) - ch_log(job->jv_channel, "Job ended"); - if (job->jv_status == JOB_ENDED && job->jv_exit_cb != NULL) - { - typval_T argv[3]; - typval_T rettv; - int dummy; - - /* invoke the exit callback; make sure the refcount is > 0 */ - ++job->jv_refcount; - argv[0].v_type = VAR_JOB; - argv[0].vval.v_job = job; - argv[1].v_type = VAR_NUMBER; - argv[1].vval.v_number = job->jv_exitval; - call_func(job->jv_exit_cb, (int)STRLEN(job->jv_exit_cb), - &rettv, 2, argv, NULL, 0L, 0L, &dummy, TRUE, - job->jv_exit_partial, NULL); - clear_tv(&rettv); - --job->jv_refcount; - channel_need_redraw = TRUE; - } - if (job->jv_status == JOB_ENDED && job->jv_refcount == 0) - { - /* The job was already unreferenced, now that it ended it can be - * freed. Careful: caller must not use "job" after this! */ - job_free(job); - } + job_cleanup(job); } return result; } diff --git a/src/os_unix.c b/src/os_unix.c index 8b0dd552d1..bc9acd4f7b 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -5294,8 +5294,7 @@ mch_job_status(job_T *job) if (wait_pid == -1) { /* process must have exited */ - job->jv_status = JOB_ENDED; - return "dead"; + goto return_dead; } if (wait_pid == 0) return "run"; @@ -5303,16 +5302,62 @@ mch_job_status(job_T *job) { /* LINTED avoid "bitwise operation on signed value" */ job->jv_exitval = WEXITSTATUS(status); - job->jv_status = JOB_ENDED; - return "dead"; + goto return_dead; } if (WIFSIGNALED(status)) { job->jv_exitval = -1; - job->jv_status = JOB_ENDED; - return "dead"; + goto return_dead; } return "run"; + +return_dead: + if (job->jv_status != JOB_ENDED) + { + ch_log(job->jv_channel, "Job ended"); + job->jv_status = JOB_ENDED; + } + return "dead"; +} + + job_T * +mch_detect_ended_job(job_T *job_list) +{ +# ifdef HAVE_UNION_WAIT + union wait status; +# else + int status = -1; +# endif + pid_t wait_pid = 0; + job_T *job; + +# ifdef __NeXT__ + wait_pid = wait4(-1, &status, WNOHANG, (struct rusage *)0); +# else + wait_pid = waitpid(-1, &status, WNOHANG); +# endif + if (wait_pid <= 0) + /* no process ended */ + return NULL; + for (job = job_list; job != NULL; job = job->jv_next) + { + if (job->jv_pid == wait_pid) + { + if (WIFEXITED(status)) + /* LINTED avoid "bitwise operation on signed value" */ + job->jv_exitval = WEXITSTATUS(status); + else if (WIFSIGNALED(status)) + job->jv_exitval = -1; + if (job->jv_status != JOB_ENDED) + { + ch_log(job->jv_channel, "Job ended"); + job->jv_status = JOB_ENDED; + } + return job; + } + } + return NULL; + } int diff --git a/src/os_win32.c b/src/os_win32.c index 9ff2f7ee40..d52beb8869 100644 --- a/src/os_win32.c +++ b/src/os_win32.c @@ -4973,13 +4973,53 @@ mch_job_status(job_T *job) if (!GetExitCodeProcess(job->jv_proc_info.hProcess, &dwExitCode) || dwExitCode != STILL_ACTIVE) { - job->jv_status = JOB_ENDED; job->jv_exitval = (int)dwExitCode; + if (job->jv_status != JOB_ENDED) + { + ch_log(job->jv_channel, "Job ended"); + job->jv_status = JOB_ENDED; + } return "dead"; } return "run"; } + job_T * +mch_detect_ended_job(job_T *job_list) +{ + HANDLE jobHandles[MAXIMUM_WAIT_OBJECTS]; + job_T *jobArray[MAXIMUM_WAIT_OBJECTS]; + job_T *job = job_list; + + while (job != NULL) + { + DWORD n; + DWORD result; + + for (n = 0; n < MAXIMUM_WAIT_OBJECTS + && job != NULL; job = job->jv_next) + { + if (job->jv_status == JOB_STARTED) + { + jobHandles[n] = job->jv_proc_info.hProcess; + jobArray[n] = job; + ++n; + } + } + if (n == 0) + continue; + result = WaitForMultipleObjects(n, jobHandles, FALSE, 0); + if (result >= WAIT_OBJECT_0 && result < WAIT_OBJECT_0 + n) + { + job_T *wait_job = jobArray[result - WAIT_OBJECT_0]; + + if (STRCMP(mch_job_status(wait_job), "dead") == 0) + return wait_job; + } + } + return NULL; +} + int mch_stop_job(job_T *job, char_u *how) { diff --git a/src/proto/os_unix.pro b/src/proto/os_unix.pro index b1e9b8e299..700b69ef2e 100644 --- a/src/proto/os_unix.pro +++ b/src/proto/os_unix.pro @@ -59,6 +59,7 @@ int mch_parse_cmd(char_u *cmd, int use_shcf, char ***argv, int *argc); int mch_call_shell(char_u *cmd, int options); void mch_start_job(char **argv, job_T *job, jobopt_T *options); char *mch_job_status(job_T *job); +job_T *mch_detect_ended_job(job_T *job_list); int mch_stop_job(job_T *job, char_u *how); void mch_clear_job(job_T *job); void mch_breakcheck(int force); diff --git a/src/proto/os_win32.pro b/src/proto/os_win32.pro index b76e3474ba..b21a7ba339 100644 --- a/src/proto/os_win32.pro +++ b/src/proto/os_win32.pro @@ -41,6 +41,7 @@ void mch_set_winsize_now(void); int mch_call_shell(char_u *cmd, int options); void mch_start_job(char *cmd, job_T *job, jobopt_T *options); char *mch_job_status(job_T *job); +job_T *mch_detect_ended_job(job_T *job_list); int mch_stop_job(job_T *job, char_u *how); void mch_clear_job(job_T *job); void mch_set_normal_colors(void); diff --git a/src/testdir/test_channel.vim b/src/testdir/test_channel.vim index fbcd496e99..e7ba667680 100644 --- a/src/testdir/test_channel.vim +++ b/src/testdir/test_channel.vim @@ -1362,6 +1362,24 @@ func Test_exit_callback() endif endfunc +let g:exit_cb_time = {'start': 0, 'end': 0} +function MyExitTimeCb(job, status) + let g:exit_cb_time.end = reltime(g:exit_cb_time.start) +endfunction + +func Test_exit_callback_interval() + if !has('job') + return + endif + + let g:exit_cb_time.start = reltime() + let job = job_start([s:python, '-c', 'import time;time.sleep(0.5)'], {'exit_cb': 'MyExitTimeCb'}) + call WaitFor('g:exit_cb_time.end != 0') + let elapsed = reltimefloat(g:exit_cb_time.end) + call assert_true(elapsed > 0.3) + call assert_true(elapsed < 1.0) +endfunc + """"""""" let g:Ch_close_ret = 'alive' diff --git a/src/version.c b/src/version.c index 513223e450..61e6979d5f 100644 --- a/src/version.c +++ b/src/version.c @@ -764,6 +764,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 36, /**/ 35, /**/ From 0a9046fbcb33770517ab0220b8100c4494bddab2 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 15 Oct 2016 19:28:13 +0200 Subject: [PATCH 5/7] patch 8.0.0037 Problem: Get E924 when switching tabs. () Solution: Use win_valid_any_tab() instead of win_valid(). (Martin Vuille, closes #1167, closes #1171) --- src/quickfix.c | 2 +- src/testdir/test_quickfix.vim | 16 ++++++++++++++++ src/version.c | 2 ++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/quickfix.c b/src/quickfix.c index 9139663483..5bd1257200 100644 --- a/src/quickfix.c +++ b/src/quickfix.c @@ -2266,7 +2266,7 @@ win_found: ok = buflist_getfile(qf_ptr->qf_fnum, (linenr_T)1, GETF_SETMARK | GETF_SWITCH, forceit); - if (qi != &ql_info && !win_valid(oldwin)) + if (qi != &ql_info && !win_valid_any_tab(oldwin)) { EMSG(_("E924: Current window was closed")); is_abort = TRUE; diff --git a/src/testdir/test_quickfix.vim b/src/testdir/test_quickfix.vim index 73ca7ca8a6..022c12a292 100644 --- a/src/testdir/test_quickfix.vim +++ b/src/testdir/test_quickfix.vim @@ -617,6 +617,22 @@ function Test_locationlist_curwin_was_closed() augroup! testgroup endfunction +function Test_locationlist_cross_tab_jump() + call writefile(['loclistfoo'], 'loclistfoo') + call writefile(['loclistbar'], 'loclistbar') + set switchbuf=usetab + + edit loclistfoo + tabedit loclistbar + silent lgrep loclistfoo loclist* + call assert_equal(1, tabpagenr()) + + enew | only | tabonly + set switchbuf&vim + call delete('loclistfoo') + call delete('loclistbar') +endfunction + " More tests for 'errorformat' function! Test_efm1() if !has('unix') diff --git a/src/version.c b/src/version.c index 61e6979d5f..95115f8233 100644 --- a/src/version.c +++ b/src/version.c @@ -764,6 +764,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 37, /**/ 36, /**/ From ca291aec99b60fe81eaab36aa718e51421bb88d5 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 15 Oct 2016 19:33:50 +0200 Subject: [PATCH 6/7] patch 8.0.0038 Problem: OPEN_CHR_FILES not defined for FreeBSD using Debian userland files. Solution: Check for __FreeBSD_kernel__. (James McCoy, closes #1166) --- src/version.c | 2 ++ src/vim.h | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/version.c b/src/version.c index 95115f8233..939a3e1387 100644 --- a/src/version.c +++ b/src/version.c @@ -764,6 +764,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 38, /**/ 37, /**/ diff --git a/src/vim.h b/src/vim.h index 0ac296e260..5946d63d2a 100644 --- a/src/vim.h +++ b/src/vim.h @@ -2495,7 +2495,8 @@ typedef enum #define FNE_INCL_BR 1 /* include [] in name */ #define FNE_CHECK_START 2 /* check name starts with valid character */ -#if (defined(sun) || defined(__FreeBSD__)) && defined(S_ISCHR) +#if (defined(sun) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)) \ + && defined(S_ISCHR) # define OPEN_CHR_FILES #endif From 156919f99afd1ac11d19d4270afbc1afb7245640 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 15 Oct 2016 20:46:20 +0200 Subject: [PATCH 7/7] patch 8.0.0039 Problem: When Vim 8 reads an old viminfo and exits, the next time marks are not read from viminfo. (Ned Batchelder) Solution: Set a mark when it wasn't set before, even when the timestamp is zero. (closes #1170) --- src/mark.c | 3 ++- src/testdir/test_viminfo.vim | 26 +++++++++++++++++++++++--- src/version.c | 2 ++ 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/mark.c b/src/mark.c index 0627a7ceab..9c84bc40dc 100644 --- a/src/mark.c +++ b/src/mark.c @@ -1597,7 +1597,8 @@ handle_viminfo_mark(garray_T *values, int force) if (fm != NULL) { - if (vi_namedfm != NULL || fm->time_set < timestamp || force) + if (vi_namedfm != NULL || fm->fmark.mark.lnum == 0 + || fm->time_set < timestamp || force) { fm->fmark.mark.lnum = lnum; fm->fmark.mark.col = col; diff --git a/src/testdir/test_viminfo.vim b/src/testdir/test_viminfo.vim index 97fd7f7a60..7d0f1570bf 100644 --- a/src/testdir/test_viminfo.vim +++ b/src/testdir/test_viminfo.vim @@ -1,6 +1,6 @@ " Test for reading and writing .viminfo -function Test_read_and_write() +function Test_viminfo_read_and_write() call histdel(':') let lines = [ \ '# comment line', @@ -17,7 +17,7 @@ function Test_read_and_write() let lines = readfile('Xviminfo') let done = 0 for line in lines - if line[0] == '|' && line !~ '^|[234],' + if line[0] == '|' && line !~ '^|[234],' && line !~ '^|<' if done == 0 call assert_equal('|1,4', line) elseif done == 1 @@ -469,7 +469,27 @@ func Test_viminfo_file_mark_tabclose() silent! bwipe Xtestfileintab endfunc -func Test_oldfiles() +func Test_viminfo_file_mark_zero_time() + let lines = [ + \ '# Viminfo version', + \ '|1,4', + \ '', + \ '*encoding=utf-8', + \ '', + \ '# File marks:', + \ "'B 1 0 /tmp/nothing", + \ '|4,66,1,0,0,"/tmp/nothing"', + \ "", + \ ] + call writefile(lines, 'Xviminfo') + delmark B + rviminfo Xviminfo + call delete('Xviminfo') + call assert_equal(1, line("'B")) + delmark B +endfunc + +func Test_viminfo_oldfiles() let v:oldfiles = [] let lines = [ \ '# comment line', diff --git a/src/version.c b/src/version.c index 939a3e1387..2bfd48507b 100644 --- a/src/version.c +++ b/src/version.c @@ -764,6 +764,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 39, /**/ 38, /**/