From 461a7fcfce3cd6414f990037e6468af3b5ccf119 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 22 Dec 2018 13:28:07 +0100 Subject: [PATCH 01/22] patch 8.1.0619: :echomsg and :echoerr do not handle List and Dict Problem: :echomsg and :echoerr do not handle List and Dict like :echo does. (Daniel Hahler) Solution: Be more tolerant about the expression result type. --- runtime/doc/eval.txt | 9 +++++---- src/eval.c | 31 ++++++++++++++++++++++++++++++- src/evalfunc.c | 3 +-- src/message.c | 5 ++++- src/proto/eval.pro | 1 + src/proto/evalfunc.pro | 1 + src/testdir/test_messages.vim | 30 +++++++++++++++++++++++++++++- src/version.c | 2 ++ 8 files changed, 73 insertions(+), 9 deletions(-) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index f76af3848a..2bc27a6295 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -9233,7 +9233,8 @@ test_ignore_error({expr}) *test_ignore_error()* error with try/catch cannot be used (because it skips over following code). {expr} is used literally, not as a pattern. - There is currently no way to revert this. + When the {expr} is the string "RESET" then the list of ignored + errors is made empty. test_null_channel() *test_null_channel()* Return a Channel that is null. Only useful for testing. @@ -10999,8 +11000,8 @@ This does NOT work: > The parsing works slightly different from |:echo|, more like |:execute|. All the expressions are first evaluated and concatenated before echoing anything. - The expressions must evaluate to a Number or String, a - Dictionary or List causes an error. + If expressions does not evaluate to a Number or + String, string() is used to turn it into a string. Uses the highlighting set by the |:echohl| command. Example: > :echomsg "It's a Zizzer Zazzer Zuzz, as you can plainly see." @@ -11011,7 +11012,7 @@ This does NOT work: > message in the |message-history|. When used in a script or function the line number will be added. Spaces are placed between the arguments as with the - :echo command. When used inside a try conditional, + |:echomsg| command. When used inside a try conditional, the message is raised as an error exception instead (see |try-echoerr|). Example: > diff --git a/src/eval.c b/src/eval.c index 1d14e1b0e5..b6463d2e70 100644 --- a/src/eval.c +++ b/src/eval.c @@ -7162,6 +7162,30 @@ tv_get_string_buf_chk(typval_T *varp, char_u *buf) return NULL; } +/* + * Turn a typeval into a string. Similar to tv_get_string_buf() but uses + * string() on Dict, List, etc. + */ + char_u * +tv_stringify(typval_T *varp, char_u *buf) +{ + if (varp->v_type == VAR_LIST + || varp->v_type == VAR_DICT + || varp->v_type == VAR_FUNC + || varp->v_type == VAR_PARTIAL + || varp->v_type == VAR_FLOAT) + { + typval_T tmp; + + f_string(varp, &tmp); + tv_get_string_buf(&tmp, buf); + clear_tv(varp); + *varp = tmp; + return tmp.vval.v_string; + } + return tv_get_string_buf(varp, buf); +} + /* * Find variable "name" in the list of variables. * Return a pointer to it if found, NULL if not found. @@ -8142,7 +8166,12 @@ ex_execute(exarg_T *eap) if (!eap->skip) { - p = tv_get_string(&rettv); + char_u buf[NUMBUFLEN]; + + if (eap->cmdidx == CMD_execute) + p = tv_get_string_buf(&rettv, buf); + else + p = tv_stringify(&rettv, buf); len = (int)STRLEN(p); if (ga_grow(&ga, len + 2) == FAIL) { diff --git a/src/evalfunc.c b/src/evalfunc.c index cbe2a4ebb5..bd2acef7fd 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -396,7 +396,6 @@ static void f_strftime(typval_T *argvars, typval_T *rettv); #endif static void f_strgetchar(typval_T *argvars, typval_T *rettv); static void f_stridx(typval_T *argvars, typval_T *rettv); -static void f_string(typval_T *argvars, typval_T *rettv); static void f_strlen(typval_T *argvars, typval_T *rettv); static void f_strcharpart(typval_T *argvars, typval_T *rettv); static void f_strpart(typval_T *argvars, typval_T *rettv); @@ -12475,7 +12474,7 @@ f_stridx(typval_T *argvars, typval_T *rettv) /* * "string()" function */ - static void + void f_string(typval_T *argvars, typval_T *rettv) { char_u *tofree; diff --git a/src/message.c b/src/message.c index f191c78a27..1c9fbe513c 100644 --- a/src/message.c +++ b/src/message.c @@ -553,7 +553,10 @@ ignore_error_for_testing(char_u *error) if (ignore_error_list.ga_itemsize == 0) ga_init2(&ignore_error_list, sizeof(char_u *), 1); - ga_add_string(&ignore_error_list, error); + if (STRCMP("RESET", error) == 0) + ga_clear_strings(&ignore_error_list); + else + ga_add_string(&ignore_error_list, error); } static int diff --git a/src/proto/eval.pro b/src/proto/eval.pro index bc6808c746..7d73f6c28c 100644 --- a/src/proto/eval.pro +++ b/src/proto/eval.pro @@ -89,6 +89,7 @@ char_u *tv_get_string(typval_T *varp); char_u *tv_get_string_buf(typval_T *varp, char_u *buf); char_u *tv_get_string_chk(typval_T *varp); char_u *tv_get_string_buf_chk(typval_T *varp, char_u *buf); +char_u *tv_stringify(typval_T *varp, char_u *buf); dictitem_T *find_var(char_u *name, hashtab_T **htp, int no_autoload); dictitem_T *find_var_in_ht(hashtab_T *ht, int htname, char_u *varname, int no_autoload); hashtab_T *find_var_ht(char_u *name, char_u **varname); diff --git a/src/proto/evalfunc.pro b/src/proto/evalfunc.pro index c79b59c0d9..09861628b5 100644 --- a/src/proto/evalfunc.pro +++ b/src/proto/evalfunc.pro @@ -9,6 +9,7 @@ void execute_redir_str(char_u *value, int value_len); void mzscheme_call_vim(char_u *name, typval_T *args, typval_T *rettv); float_T vim_round(float_T f); long do_searchpair(char_u *spat, char_u *mpat, char_u *epat, int dir, typval_T *skip, int flags, pos_T *match_pos, linenr_T lnum_stop, long time_limit); +void f_string(typval_T *argvars, typval_T *rettv); char_u *get_callback(typval_T *arg, partial_T **pp); void free_callback(char_u *callback, partial_T *partial); /* vim: set ft=c : */ diff --git a/src/testdir/test_messages.vim b/src/testdir/test_messages.vim index de14b81f9a..0a7f3f59a2 100644 --- a/src/testdir/test_messages.vim +++ b/src/testdir/test_messages.vim @@ -1,4 +1,4 @@ -" Tests for :messages +" Tests for :messages, :echomsg, :echoerr function Test_messages() let oldmore = &more @@ -64,3 +64,31 @@ func Test_message_completion() call feedkeys(":message \\\"\", 'tx') call assert_equal('"message clear', @:) endfunc + +func Test_echomsg() + call assert_equal("\nhello", execute(':echomsg "hello"')) + call assert_equal("\n", execute(':echomsg ""')) + call assert_equal("\n12345", execute(':echomsg 12345')) + call assert_equal("\n[]", execute(':echomsg []')) + call assert_equal("\n[1, 2, 3]", execute(':echomsg [1, 2, 3]')) + call assert_equal("\n{}", execute(':echomsg {}')) + call assert_equal("\n{'a': 1, 'b': 2}", execute(':echomsg {"a": 1, "b": 2}')) + if has('float') + call assert_equal("\n1.23", execute(':echomsg 1.23')) + endif + call assert_match("function('\\d*')", execute(':echomsg {-> 1234}')) +endfunc + +func Test_echoerr() + call test_ignore_error('IgNoRe') + call assert_equal("\nIgNoRe hello", execute(':echoerr "IgNoRe hello"')) + call assert_equal("\n12345 IgNoRe", execute(':echoerr 12345 "IgNoRe"')) + call assert_equal("\n[1, 2, 'IgNoRe']", execute(':echoerr [1, 2, "IgNoRe"]')) + call assert_equal("\n{'IgNoRe': 2, 'a': 1}", execute(':echoerr {"a": 1, "IgNoRe": 2}')) + if has('float') + call assert_equal("\n1.23 IgNoRe", execute(':echoerr 1.23 "IgNoRe"')) + endif + call test_ignore_error('') + call assert_match("function('\\d*')", execute(':echoerr {-> 1234}')) + call test_ignore_error('RESET') +endfunc diff --git a/src/version.c b/src/version.c index 1612f9dd1a..b1a28c1faa 100644 --- a/src/version.c +++ b/src/version.c @@ -799,6 +799,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 619, /**/ 618, /**/ From 3ac55c86449de57f63fa1cc2ac19202c1aa1ebb9 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 22 Dec 2018 14:59:03 +0100 Subject: [PATCH 02/22] patch 8.1.0620: overuling CONF_ARGS from the environment no longer works Problem: Overuling CONF_ARGS from the environment no longer works. (Tony Mechelynck) Solution: Do not define any CONF_ARGS by default. --- src/Makefile | 7 ++++--- src/version.c | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Makefile b/src/Makefile index d8667373b8..56e3ba61fc 100644 --- a/src/Makefile +++ b/src/Makefile @@ -323,10 +323,9 @@ CClink = $(CC) #CONF_ARGS3 = --with-global-runtime=/etc/vim,/usr/share/vim #CONF_ARGS4 = --with-local-dir=/usr/share #CONF_ARGS5 = --without-local-dir -CONF_ARGS = $(CONF_ARGS1) $(CONF_ARGS2) $(CONF_ARGS3) $(CONF_ARGS4) $(CONF_ARGS5) # Use this one if you distribute a modified version of Vim. -#CONF_ARGS = --with-modified-by="John Doe" +#CONF_ARGS6 = --with-modified-by="John Doe" # GUI - For creating Vim with GUI (gvim) (B) # Uncomment this line when you don't want to get the GUI version, although you @@ -1933,7 +1932,9 @@ config auto/config.mk: auto/configure config.mk.in config.h.in $(CONF_OPT_FEAT) $(CONF_TERM_LIB) \ $(CONF_OPT_COMPBY) $(CONF_OPT_ACL) $(CONF_OPT_NETBEANS) \ $(CONF_OPT_CHANNEL) $(CONF_OPT_TERMINAL) \ - $(CONF_ARGS) $(CONF_OPT_MZSCHEME) $(CONF_OPT_PLTHOME) \ + $(CONF_ARGS1) $(CONF_ARGS2) $(CONF_ARGS3) $(CONF_ARGS4) \ + $(CONF_ARGS5) $(CONF_ARGS6) \ + $(CONF_OPT_MZSCHEME) $(CONF_OPT_PLTHOME) \ $(CONF_OPT_LUA) $(CONF_OPT_LUA_PREFIX) \ $(CONF_OPT_SYSMOUSE); \ fi diff --git a/src/version.c b/src/version.c index b1a28c1faa..333a98fc3c 100644 --- a/src/version.c +++ b/src/version.c @@ -799,6 +799,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 620, /**/ 619, /**/ From ef3c6a5b023723a5f6eec47328cf7139c2048f8c Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 22 Dec 2018 15:14:49 +0100 Subject: [PATCH 03/22] patch 8.1.0621: terminal debugger does not handle unexpected debugger exit Problem: Terminal debugger does not handle unexpected debugger exit. Solution: Check for debugger job ended and close unused buffers. (Damien) --- .../dist/opt/termdebug/plugin/termdebug.vim | 22 ++++++++++++++----- src/version.c | 2 ++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim index df89abd7ad..846897004f 100644 --- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim +++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim @@ -142,6 +142,13 @@ func s:StartDebug_internal(dict) endif endfunc +" Use when debugger didn't start or ended. +func s:CloseBuffers() + exe 'bwipe! ' . s:ptybuf + exe 'bwipe! ' . s:commbuf + unlet! s:gdbwin +endfunc + func s:StartDebug_term(dict) " Open a terminal window without a job, to run the debugged program in. let s:ptybuf = term_start('NONE', { @@ -181,13 +188,11 @@ func s:StartDebug_term(dict) let cmd = [g:termdebugger, '-quiet', '-tty', pty] + gdb_args call ch_log('executing "' . join(cmd) . '"') let s:gdbbuf = term_start(cmd, { - \ 'exit_cb': function('s:EndTermDebug'), \ 'term_finish': 'close', \ }) if s:gdbbuf == 0 echoerr 'Failed to open the gdb terminal window' - exe 'bwipe! ' . s:ptybuf - exe 'bwipe! ' . s:commbuf + call s:CloseBuffers() return endif let s:gdbwin = win_getid(winnr()) @@ -204,6 +209,13 @@ func s:StartDebug_term(dict) " why the debugger doesn't work. let try_count = 0 while 1 + let gdbproc = term_getjob(s:gdbbuf) + if gdbproc == v:null || job_status(gdbproc) !=# 'run' + echoerr string(g:termdebugger) . ' exited unexpectedly' + call s:CloseBuffers() + return + endif + let response = '' for lnum in range(1,200) if term_getline(s:gdbbuf, lnum) =~ 'new-ui mi ' @@ -211,8 +223,7 @@ func s:StartDebug_term(dict) let response = term_getline(s:gdbbuf, lnum) . term_getline(s:gdbbuf, lnum + 1) if response =~ 'Undefined command' echoerr 'Sorry, your gdb is too old, gdb 7.12 is required' - exe 'bwipe! ' . s:ptybuf - exe 'bwipe! ' . s:commbuf + call s:CloseBuffers() return endif if response =~ 'New UI allocated' @@ -243,6 +254,7 @@ func s:StartDebug_term(dict) " "Type to continue" prompt. call s:SendCommand('set pagination off') + call job_setoptions(gdbproc, {'exit_cb': function('s:EndTermDebug')}) call s:StartDebugCommon(a:dict) endfunc diff --git a/src/version.c b/src/version.c index 333a98fc3c..40c3c5a2f3 100644 --- a/src/version.c +++ b/src/version.c @@ -799,6 +799,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 621, /**/ 620, /**/ From 9752c72f492312acd1c84e673864faed31a3bc97 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 22 Dec 2018 16:49:34 +0100 Subject: [PATCH 04/22] patch 8.1.0622: adding quickfix items marks items as valid errors Problem: Adding quickfix items marks items as valid errors. (Daniel Hahler) Solution: Check when items are valid. (Yegappan Lakshmanan, closes #3683, closes #3633) --- src/quickfix.c | 31 +++++++++++++++++++++---------- src/testdir/test_quickfix.vim | 22 ++++++++++++++++++++++ src/version.c | 2 ++ 3 files changed, 45 insertions(+), 10 deletions(-) diff --git a/src/quickfix.c b/src/quickfix.c index 5291fb1115..71fe4939ff 100644 --- a/src/quickfix.c +++ b/src/quickfix.c @@ -6241,14 +6241,16 @@ qf_get_properties(win_T *wp, dict_T *what, dict_T *retdict) /* * Add a new quickfix entry to list at 'qf_idx' in the stack 'qi' from the - * items in the dict 'd'. + * items in the dict 'd'. If it is a valid error entry, then set 'valid_entry' + * to TRUE. */ static int qf_add_entry_from_dict( qf_info_T *qi, int qf_idx, dict_T *d, - int first_entry) + int first_entry, + int *valid_entry) { static int did_bufnr_emsg; char_u *filename, *module, *pattern, *text, *type; @@ -6313,6 +6315,9 @@ qf_add_entry_from_dict( vim_free(text); vim_free(type); + if (valid) + *valid_entry = TRUE; + return status; } @@ -6333,6 +6338,7 @@ qf_add_entries( dict_T *d; qfline_T *old_last = NULL; int retval = OK; + int valid_entry = FALSE; if (action == ' ' || qf_idx == qi->qf_listcount) { @@ -6359,22 +6365,27 @@ qf_add_entries( if (d == NULL) continue; - retval = qf_add_entry_from_dict(qi, qf_idx, d, li == list->lv_first); + retval = qf_add_entry_from_dict(qi, qf_idx, d, li == list->lv_first, + &valid_entry); if (retval == FAIL) break; } - if (qfl->qf_index == 0) + // Check if any valid error entries are added to the list. + if (valid_entry) + qfl->qf_nonevalid = FALSE; + else if (qfl->qf_index == 0) // no valid entry qfl->qf_nonevalid = TRUE; - else - qfl->qf_nonevalid = FALSE; + + // If not appending to the list, set the current error to the first entry if (action != 'a') - { qfl->qf_ptr = qfl->qf_start; - if (!qf_list_empty(qi, qf_idx)) - qfl->qf_index = 1; - } + + // Update the current error index if not appending to the list or if the + // list was empty before and it is not empty now. + if ((action != 'a' || qfl->qf_index == 0) && !qf_list_empty(qi, qf_idx)) + qfl->qf_index = 1; // Don't update the cursor in quickfix window when appending entries qf_update_buffer(qi, old_last); diff --git a/src/testdir/test_quickfix.vim b/src/testdir/test_quickfix.vim index be03b41e0f..7fdfc73aaa 100644 --- a/src/testdir/test_quickfix.vim +++ b/src/testdir/test_quickfix.vim @@ -1299,6 +1299,28 @@ func SetXlistTests(cchar, bnum) let l = g:Xgetlist() call g:Xsetlist(l) call assert_equal(0, g:Xgetlist()[0].valid) + " Adding a non-valid entry should not mark the list as having valid entries + call g:Xsetlist([{'bufnr':a:bnum, 'lnum':5, 'valid':0}], 'a') + Xwindow + call assert_equal(1, winnr('$')) + + " :cnext/:cprev should still work even with invalid entries in the list + let l = [{'bufnr' : a:bnum, 'lnum' : 1, 'text' : '1', 'valid' : 0}, + \ {'bufnr' : a:bnum, 'lnum' : 2, 'text' : '2', 'valid' : 0}] + call g:Xsetlist(l) + Xnext + call assert_equal(2, g:Xgetlist({'idx' : 0}).idx) + Xprev + call assert_equal(1, g:Xgetlist({'idx' : 0}).idx) + " :cnext/:cprev should still work after appending invalid entries to an + " empty list + call g:Xsetlist([]) + call g:Xsetlist(l, 'a') + Xnext + call assert_equal(2, g:Xgetlist({'idx' : 0}).idx) + Xprev + call assert_equal(1, g:Xgetlist({'idx' : 0}).idx) + call g:Xsetlist([{'text':'Text1', 'valid':1}]) Xwindow call assert_equal(2, winnr('$')) diff --git a/src/version.c b/src/version.c index 40c3c5a2f3..bb0c81f7da 100644 --- a/src/version.c +++ b/src/version.c @@ -799,6 +799,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 622, /**/ 621, /**/ From 3d1491ed2394b3e92902102879bace28a5f9c201 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 22 Dec 2018 17:07:50 +0100 Subject: [PATCH 05/22] patch 8.1.0623: iterating through window frames is repeated Problem: Iterating through window frames is repeated. Solution: Define FOR_ALL_FRAMES. (Yegappan Lakshmanan) --- src/ex_docmd.c | 4 +-- src/globals.h | 2 ++ src/screen.c | 2 +- src/version.c | 2 ++ src/window.c | 67 +++++++++++++++++++++++--------------------------- 5 files changed, 38 insertions(+), 39 deletions(-) diff --git a/src/ex_docmd.c b/src/ex_docmd.c index 466e77e9ee..d3246f439b 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -11688,7 +11688,7 @@ ses_skipframe(frame_T *fr) { frame_T *frc; - for (frc = fr; frc != NULL; frc = frc->fr_next) + FOR_ALL_FRAMES(frc, fr) if (ses_do_frame(frc)) break; return frc; @@ -11705,7 +11705,7 @@ ses_do_frame(frame_T *fr) if (fr->fr_layout == FR_LEAF) return ses_do_win(fr->fr_win); - for (frc = fr->fr_child; frc != NULL; frc = frc->fr_next) + FOR_ALL_FRAMES(frc, fr->fr_child) if (ses_do_frame(frc)) return TRUE; return FALSE; diff --git a/src/globals.h b/src/globals.h index 67fcb4b058..0a29511434 100644 --- a/src/globals.h +++ b/src/globals.h @@ -564,6 +564,8 @@ EXTERN win_T *prevwin INIT(= NULL); /* previous window */ # define ONE_WINDOW (firstwin == lastwin) # define W_NEXT(wp) ((wp)->w_next) # define FOR_ALL_WINDOWS(wp) for (wp = firstwin; wp != NULL; wp = wp->w_next) +# define FOR_ALL_FRAMES(frp, first_frame) \ + for (frp = first_frame; frp != NULL; frp = frp->fr_next) # define FOR_ALL_TABPAGES(tp) for (tp = first_tabpage; tp != NULL; tp = tp->tp_next) # define FOR_ALL_WINDOWS_IN_TAB(tp, wp) \ for ((wp) = ((tp) == NULL || (tp) == curtab) \ diff --git a/src/screen.c b/src/screen.c index 4a61807c92..ed1d4a3914 100644 --- a/src/screen.c +++ b/src/screen.c @@ -6681,7 +6681,7 @@ win_redraw_last_status(frame_T *frp) frp->fr_win->w_redr_status = TRUE; else if (frp->fr_layout == FR_ROW) { - for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next) + FOR_ALL_FRAMES(frp, frp->fr_child) win_redraw_last_status(frp); } else /* frp->fr_layout == FR_COL */ diff --git a/src/version.c b/src/version.c index bb0c81f7da..f57ffb3ffd 100644 --- a/src/version.c +++ b/src/version.c @@ -799,6 +799,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 623, /**/ 622, /**/ diff --git a/src/window.c b/src/window.c index cba9785c3f..b9617f0371 100644 --- a/src/window.c +++ b/src/window.c @@ -836,8 +836,7 @@ win_split_ins( frp = frp->fr_parent) { if (frp->fr_layout == FR_ROW) - for (frp2 = frp->fr_child; frp2 != NULL; - frp2 = frp2->fr_next) + FOR_ALL_FRAMES(frp2, frp->fr_child) if (frp2 != prevfrp) minwidth += frame_minwidth(frp2, NOWIN); prevfrp = frp; @@ -920,8 +919,7 @@ win_split_ins( frp = frp->fr_parent) { if (frp->fr_layout == FR_COL) - for (frp2 = frp->fr_child; frp2 != NULL; - frp2 = frp2->fr_next) + FOR_ALL_FRAMES(frp2, frp->fr_child) if (frp2 != prevfrp) minheight += frame_minheight(frp2, NOWIN); prevfrp = frp; @@ -1078,7 +1076,7 @@ win_split_ins( if (frp->fr_win != NULL) oldwin->w_frame = frp; else - for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next) + FOR_ALL_FRAMES(frp, frp->fr_child) frp->fr_parent = curfrp; } @@ -1605,8 +1603,7 @@ win_rotate(int upwards, int count) #endif /* Check if all frames in this row/col have one window. */ - for (frp = curwin->w_frame->fr_parent->fr_child; frp != NULL; - frp = frp->fr_next) + FOR_ALL_FRAMES(frp, curwin->w_frame->fr_parent->fr_child) if (frp->fr_win == NULL) { EMSG(_("E443: Cannot rotate when another window is split")); @@ -1856,7 +1853,7 @@ win_equal_rec( else { next_curwin_size = -1; - for (fr = topfr->fr_child; fr != NULL; fr = fr->fr_next) + FOR_ALL_FRAMES(fr, topfr->fr_child) { /* If 'winfixwidth' set keep the window width if * possible. @@ -1909,7 +1906,7 @@ win_equal_rec( --totwincount; /* don't count curwin */ } - for (fr = topfr->fr_child; fr != NULL; fr = fr->fr_next) + FOR_ALL_FRAMES(fr, topfr->fr_child) { wincount = 1; if (fr->fr_next == NULL) @@ -1997,7 +1994,7 @@ win_equal_rec( else { next_curwin_size = -1; - for (fr = topfr->fr_child; fr != NULL; fr = fr->fr_next) + FOR_ALL_FRAMES(fr, topfr->fr_child) { /* If 'winfixheight' set keep the window height if * possible. @@ -2050,7 +2047,7 @@ win_equal_rec( --totwincount; /* don't count curwin */ } - for (fr = topfr->fr_child; fr != NULL; fr = fr->fr_next) + FOR_ALL_FRAMES(fr, topfr->fr_child) { wincount = 1; if (fr->fr_next == NULL) @@ -2751,7 +2748,7 @@ winframe_remove( * and remove it. */ frp2->fr_parent->fr_layout = frp2->fr_layout; frp2->fr_parent->fr_child = frp2->fr_child; - for (frp = frp2->fr_child; frp != NULL; frp = frp->fr_next) + FOR_ALL_FRAMES(frp, frp2->fr_child) frp->fr_parent = frp2->fr_parent; frp2->fr_parent->fr_win = frp2->fr_win; if (frp2->fr_win != NULL) @@ -2883,7 +2880,7 @@ frame_has_win(frame_T *frp, win_T *wp) if (frp->fr_layout == FR_LEAF) return frp->fr_win == wp; - for (p = frp->fr_child; p != NULL; p = p->fr_next) + FOR_ALL_FRAMES(p, frp->fr_child) if (frame_has_win(p, wp)) return TRUE; return FALSE; @@ -2917,7 +2914,7 @@ frame_new_height( do { /* All frames in this row get the same new height. */ - for (frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next) + FOR_ALL_FRAMES(frp, topfrp->fr_child) { frame_new_height(frp, height, topfirst, wfh); if (frp->fr_height > height) @@ -3014,7 +3011,7 @@ frame_fixed_height(frame_T *frp) { /* The frame is fixed height if one of the frames in the row is fixed * height. */ - for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next) + FOR_ALL_FRAMES(frp, frp->fr_child) if (frame_fixed_height(frp)) return TRUE; return FALSE; @@ -3022,7 +3019,7 @@ frame_fixed_height(frame_T *frp) /* frp->fr_layout == FR_COL: The frame is fixed height if all of the * frames in the row are fixed height. */ - for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next) + FOR_ALL_FRAMES(frp, frp->fr_child) if (!frame_fixed_height(frp)) return FALSE; return TRUE; @@ -3043,7 +3040,7 @@ frame_fixed_width(frame_T *frp) { /* The frame is fixed width if one of the frames in the row is fixed * width. */ - for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next) + FOR_ALL_FRAMES(frp, frp->fr_child) if (frame_fixed_width(frp)) return TRUE; return FALSE; @@ -3051,7 +3048,7 @@ frame_fixed_width(frame_T *frp) /* frp->fr_layout == FR_ROW: The frame is fixed width if all of the * frames in the row are fixed width. */ - for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next) + FOR_ALL_FRAMES(frp, frp->fr_child) if (!frame_fixed_width(frp)) return FALSE; return TRUE; @@ -3079,7 +3076,7 @@ frame_add_statusline(frame_T *frp) else if (frp->fr_layout == FR_ROW) { /* Handle all the frames in the row. */ - for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next) + FOR_ALL_FRAMES(frp, frp->fr_child) frame_add_statusline(frp); } else /* frp->fr_layout == FR_COL */ @@ -3125,7 +3122,7 @@ frame_new_width( do { /* All frames in this column get the same new width. */ - for (frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next) + FOR_ALL_FRAMES(frp, topfrp->fr_child) { frame_new_width(frp, width, leftfirst, wfw); if (frp->fr_width > width) @@ -3228,7 +3225,7 @@ frame_add_vsep(frame_T *frp) else if (frp->fr_layout == FR_COL) { /* Handle all the frames in the column. */ - for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next) + FOR_ALL_FRAMES(frp, frp->fr_child) frame_add_vsep(frp); } else /* frp->fr_layout == FR_ROW */ @@ -3295,7 +3292,7 @@ frame_minheight(frame_T *topfrp, win_T *next_curwin) { /* get the minimal height from each frame in this row */ m = 0; - for (frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next) + FOR_ALL_FRAMES(frp, topfrp->fr_child) { n = frame_minheight(frp, next_curwin); if (n > m) @@ -3306,7 +3303,7 @@ frame_minheight(frame_T *topfrp, win_T *next_curwin) { /* Add up the minimal heights for all frames in this column. */ m = 0; - for (frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next) + FOR_ALL_FRAMES(frp, topfrp->fr_child) m += frame_minheight(frp, next_curwin); } @@ -3344,7 +3341,7 @@ frame_minwidth( { /* get the minimal width from each frame in this column */ m = 0; - for (frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next) + FOR_ALL_FRAMES(frp, topfrp->fr_child) { n = frame_minwidth(frp, next_curwin); if (n > m) @@ -3355,7 +3352,7 @@ frame_minwidth( { /* Add up the minimal widths for all frames in this row. */ m = 0; - for (frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next) + FOR_ALL_FRAMES(frp, topfrp->fr_child) m += frame_minwidth(frp, next_curwin); } @@ -5023,7 +5020,7 @@ frame_comp_pos(frame_T *topfrp, int *row, int *col) { startrow = *row; startcol = *col; - for (frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next) + FOR_ALL_FRAMES(frp, topfrp->fr_child) { if (topfrp->fr_layout == FR_ROW) *row = startrow; /* all frames are at the same row */ @@ -5143,8 +5140,7 @@ frame_setheight(frame_T *curfrp, int height) { room = 0; room_reserved = 0; - for (frp = curfrp->fr_parent->fr_child; frp != NULL; - frp = frp->fr_next) + FOR_ALL_FRAMES(frp, curfrp->fr_parent->fr_child) { if (frp != curfrp && frp->fr_win != NULL @@ -5337,8 +5333,7 @@ frame_setwidth(frame_T *curfrp, int width) { room = 0; room_reserved = 0; - for (frp = curfrp->fr_parent->fr_child; frp != NULL; - frp = frp->fr_next) + FOR_ALL_FRAMES(frp, curfrp->fr_parent->fr_child) { if (frp != curfrp && frp->fr_win != NULL @@ -5562,7 +5557,7 @@ win_drag_status_line(win_T *dragwin, int offset) if (room < 0) room = 0; /* sum up the room of frames below of the current one */ - for (fr = curfr->fr_next; fr != NULL; fr = fr->fr_next) + FOR_ALL_FRAMES(fr, curfr->fr_next) room += fr->fr_height - frame_minheight(fr, NULL); fr = curfr; /* put fr at window that grows */ } @@ -5676,7 +5671,7 @@ win_drag_vsep_line(win_T *dragwin, int offset) left = FALSE; /* sum up the room of frames right of the current one */ room = 0; - for (fr = curfr->fr_next; fr != NULL; fr = fr->fr_next) + FOR_ALL_FRAMES(fr, curfr->fr_next) room += fr->fr_width - frame_minwidth(fr, NULL); fr = curfr; /* put fr at window that grows */ } @@ -6073,7 +6068,7 @@ last_status_rec(frame_T *fr, int statusline) else if (fr->fr_layout == FR_ROW) { /* vertically split windows, set status line for each one */ - for (fp = fr->fr_child; fp != NULL; fp = fp->fr_next) + FOR_ALL_FRAMES(fp, fr->fr_child) last_status_rec(fp, statusline); } else @@ -6751,7 +6746,7 @@ win_hasvertsplit(void) return TRUE; if (topframe->fr_layout == FR_COL) - for (fr = topframe->fr_child; fr != NULL; fr = fr->fr_next) + FOR_ALL_FRAMES(fr, topframe->fr_child) if (fr->fr_layout == FR_ROW) return TRUE; @@ -7097,7 +7092,7 @@ frame_check_height(frame_T *topfrp, int height) return FALSE; if (topfrp->fr_layout == FR_ROW) - for (frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next) + FOR_ALL_FRAMES(frp, topfrp->fr_child) if (frp->fr_height != height) return FALSE; @@ -7116,7 +7111,7 @@ frame_check_width(frame_T *topfrp, int width) return FALSE; if (topfrp->fr_layout == FR_COL) - for (frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next) + FOR_ALL_FRAMES(frp, topfrp->fr_child) if (frp->fr_width != width) return FALSE; From 39b5d8b514a0022899a09ba097997099d861dcff Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 22 Dec 2018 17:27:15 +0100 Subject: [PATCH 06/22] patch 8.1.0624: overuling CONF_ARGS from the environment still does not work Problem: Overuling CONF_ARGS from the environment still does not work. (Tony Mechelynck) Solution: Add back CONF_ARGS next to the new numbered ones. --- src/Makefile | 4 ++-- src/version.c | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Makefile b/src/Makefile index 56e3ba61fc..38bb1090da 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1932,8 +1932,8 @@ config auto/config.mk: auto/configure config.mk.in config.h.in $(CONF_OPT_FEAT) $(CONF_TERM_LIB) \ $(CONF_OPT_COMPBY) $(CONF_OPT_ACL) $(CONF_OPT_NETBEANS) \ $(CONF_OPT_CHANNEL) $(CONF_OPT_TERMINAL) \ - $(CONF_ARGS1) $(CONF_ARGS2) $(CONF_ARGS3) $(CONF_ARGS4) \ - $(CONF_ARGS5) $(CONF_ARGS6) \ + $(CONF_ARGS) $(CONF_ARGS1) $(CONF_ARGS2) $(CONF_ARGS3) \ + $(CONF_ARGS4) $(CONF_ARGS5) $(CONF_ARGS6) \ $(CONF_OPT_MZSCHEME) $(CONF_OPT_PLTHOME) \ $(CONF_OPT_LUA) $(CONF_OPT_LUA_PREFIX) \ $(CONF_OPT_SYSMOUSE); \ diff --git a/src/version.c b/src/version.c index f57ffb3ffd..3321974a5b 100644 --- a/src/version.c +++ b/src/version.c @@ -799,6 +799,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 624, /**/ 623, /**/ From 81df63537e281da38b45bc1806e2b4582e804242 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 22 Dec 2018 18:25:30 +0100 Subject: [PATCH 07/22] patch 8.1.0625: MS-Windows: terminal test fails in white console Problem: MS-Windows: terminal test fails in white console. Solution: Accept both white and black background colors. --- src/testdir/test_terminal.vim | 12 +++++++++--- src/version.c | 2 ++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/testdir/test_terminal.vim b/src/testdir/test_terminal.vim index 220b1d13ee..f31c221e1f 100644 --- a/src/testdir/test_terminal.vim +++ b/src/testdir/test_terminal.vim @@ -159,10 +159,16 @@ func Check_123(buf) call assert_equal('2', l[1].chars) call assert_equal('3', l[2].chars) call assert_equal('#00e000', l[0].fg) - if &background == 'light' - call assert_equal('#ffffff', l[0].bg) + if has('win32') + " On Windows 'background' always defaults to dark, even though the terminal + " may use a light background. Therefore accept both white and black. + call assert_match('#ffffff\|#000000', l[0].bg) else - call assert_equal('#000000', l[0].bg) + if &background == 'light' + call assert_equal('#ffffff', l[0].bg) + else + call assert_equal('#000000', l[0].bg) + endif endif let l = term_getline(a:buf, -1) diff --git a/src/version.c b/src/version.c index 3321974a5b..594be524d9 100644 --- a/src/version.c +++ b/src/version.c @@ -799,6 +799,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 625, /**/ 624, /**/ From 4814ccbdf0e99e2d33e1ac926c59f677f5c2afe2 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 22 Dec 2018 18:44:53 +0100 Subject: [PATCH 08/22] patch 8.1.0626: MS-Windows: no resize to fit parent when using --windowid Problem: MS-Windows: no resize to fit parent when using --windowid. Solution: Pass FALSE for "mustset" in gui_set_shellsize(). (Agorgianitis Loukas, closes #3616) --- src/gui.c | 7 ++++--- src/version.c | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/gui.c b/src/gui.c index 9160848c8e..0b911b314e 100644 --- a/src/gui.c +++ b/src/gui.c @@ -687,9 +687,10 @@ gui_init(void) gui.shell_created = TRUE; #ifndef FEAT_GUI_GTK - /* Set the shell size, adjusted for the screen size. For GTK this only - * works after the shell has been opened, thus it is further down. */ - gui_set_shellsize(TRUE, TRUE, RESIZE_BOTH); + // Set the shell size, adjusted for the screen size. For GTK this only + // works after the shell has been opened, thus it is further down. + // For MS-Windows pass FALSE for "mustset" to make --windowid work. + gui_set_shellsize(FALSE, TRUE, RESIZE_BOTH); #endif #if defined(FEAT_GUI_MOTIF) && defined(FEAT_MENU) /* Need to set the size of the menubar after all the menus have been diff --git a/src/version.c b/src/version.c index 594be524d9..4d39dde9d3 100644 --- a/src/version.c +++ b/src/version.c @@ -799,6 +799,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 626, /**/ 625, /**/ From 9123c0b31a283f460ed2b6af95080120cf528118 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 22 Dec 2018 18:59:06 +0100 Subject: [PATCH 09/22] patch 8.1.0627: Python cannot handle function name of script-local function Problem: Python cannot handle function name of script-local function. Solution: Use instead of the special byte code. (Ozaki Kiichi, closes #3681) --- src/if_py_both.h | 30 +++++++++++++++++++++++++----- src/testdir/test_python2.vim | 27 +++++++++++++++++++++++++++ src/testdir/test_python3.vim | 27 +++++++++++++++++++++++++++ src/version.c | 2 ++ 4 files changed, 81 insertions(+), 5 deletions(-) diff --git a/src/if_py_both.h b/src/if_py_both.h index cdd7460191..1a4ef462ef 100644 --- a/src/if_py_both.h +++ b/src/if_py_both.h @@ -2922,8 +2922,7 @@ FunctionNew(PyTypeObject *subtype, char_u *name, int argc, typval_T *argv, { FunctionObject *self; - self = (FunctionObject *) subtype->tp_alloc(subtype, 0); - + self = (FunctionObject *)subtype->tp_alloc(subtype, 0); if (self == NULL) return NULL; @@ -2938,15 +2937,36 @@ FunctionNew(PyTypeObject *subtype, char_u *name, int argc, typval_T *argv, self->name = vim_strsave(name); } else - if ((self->name = get_expanded_name(name, - vim_strchr(name, AUTOLOAD_CHAR) == NULL)) - == NULL) + { + char_u *p; + + if ((p = get_expanded_name(name, + vim_strchr(name, AUTOLOAD_CHAR) == NULL)) == NULL) { PyErr_FORMAT(PyExc_ValueError, N_("function %s does not exist"), name); return NULL; } + if (p[0] == K_SPECIAL && p[1] == KS_EXTRA && p[2] == (int)KE_SNR) + { + char_u *np; + size_t len = STRLEN(p) + 1; + + if ((np = alloc(len + 2)) == NULL) + { + vim_free(p); + return NULL; + } + mch_memmove(np, "", 5); + mch_memmove(np + 5, p + 3, len - 3); + vim_free(p); + self->name = np; + } + else + self->name = p; + } + func_ref(self->name); self->argc = argc; self->argv = argv; diff --git a/src/testdir/test_python2.vim b/src/testdir/test_python2.vim index c438ba0415..d79400de71 100644 --- a/src/testdir/test_python2.vim +++ b/src/testdir/test_python2.vim @@ -36,3 +36,30 @@ func Test_set_cursor() normal j call assert_equal([2, 6], [line('.'), col('.')]) endfunc + +func Test_vim_function() + " Check creating vim.Function object + py import vim + + func s:foo() + return matchstr(expand(''), '\zs\d\+_foo$') + endfunc + let name = '' . s:foo() + + try + py f = vim.bindeval('function("s:foo")') + call assert_equal(name, pyeval('f.name')) + catch + call assert_false(v:exception) + endtry + + try + py f = vim.Function('\x80\xfdR' + vim.eval('s:foo()')) + call assert_equal(name, pyeval('f.name')) + catch + call assert_false(v:exception) + endtry + + py del f + delfunc s:foo +endfunc diff --git a/src/testdir/test_python3.vim b/src/testdir/test_python3.vim index 69ad802131..344034af00 100644 --- a/src/testdir/test_python3.vim +++ b/src/testdir/test_python3.vim @@ -36,3 +36,30 @@ func Test_set_cursor() normal j call assert_equal([2, 6], [line('.'), col('.')]) endfunc + +func Test_vim_function() + " Check creating vim.Function object + py3 import vim + + func s:foo() + return matchstr(expand(''), '\zs\d\+_foo$') + endfunc + let name = '' . s:foo() + + try + py3 f = vim.bindeval('function("s:foo")') + call assert_equal(name, py3eval('f.name')) + catch + call assert_false(v:exception) + endtry + + try + py3 f = vim.Function(b'\x80\xfdR' + vim.eval('s:foo()').encode()) + call assert_equal(name, py3eval('f.name')) + catch + call assert_false(v:exception) + endtry + + py3 del f + delfunc s:foo +endfunc diff --git a/src/version.c b/src/version.c index 4d39dde9d3..101d7864d1 100644 --- a/src/version.c +++ b/src/version.c @@ -799,6 +799,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 627, /**/ 626, /**/ From 80dae04d690d9aba26d443d4a19f3bd45ed0990b Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 23 Dec 2018 13:36:40 +0100 Subject: [PATCH 10/22] patch 8.1.0628: Compiler warning on MS-Windows. Problem: Compiler warning on MS-Windows. Solution: Add type cast. (Mike Williams) --- src/if_py_both.h | 2 +- src/version.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/if_py_both.h b/src/if_py_both.h index 1a4ef462ef..1c159260c7 100644 --- a/src/if_py_both.h +++ b/src/if_py_both.h @@ -2953,7 +2953,7 @@ FunctionNew(PyTypeObject *subtype, char_u *name, int argc, typval_T *argv, char_u *np; size_t len = STRLEN(p) + 1; - if ((np = alloc(len + 2)) == NULL) + if ((np = alloc((int)len + 2)) == NULL) { vim_free(p); return NULL; diff --git a/src/version.c b/src/version.c index 101d7864d1..ff22c6d502 100644 --- a/src/version.c +++ b/src/version.c @@ -799,6 +799,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 628, /**/ 627, /**/ From c33181c44ccb86637e011f35cc0397a2d76e23ae Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 23 Dec 2018 15:43:28 +0100 Subject: [PATCH 11/22] Ignore output files from indent tests. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index f31e233bb7..5691e22d00 100644 --- a/.gitignore +++ b/.gitignore @@ -80,6 +80,7 @@ src/testdir/dostmp/* src/testdir/messages src/testdir/viminfo src/testdir/opt_test.vim +runtime/indent/testdir/*.out src/memfile_test src/json_test src/message_test From 5d24a2257e597fd752e33b2c1e9c19cf9114a517 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 23 Dec 2018 19:10:09 +0100 Subject: [PATCH 12/22] patch 8.1.0629: "gn" selects the wrong text with a multi-line match Problem: "gn" selects the wrong text with a multi-line match. Solution: Get the end position from searchit() directly. (closes #3695) --- src/edit.c | 2 +- src/evalfunc.c | 4 +-- src/ex_docmd.c | 2 +- src/ex_getln.c | 2 +- src/normal.c | 2 +- src/proto/search.pro | 2 +- src/search.c | 79 +++++++++++++++++------------------------ src/testdir/test_gn.vim | 19 ++++++++++ src/version.c | 2 ++ 9 files changed, 60 insertions(+), 54 deletions(-) diff --git a/src/edit.c b/src/edit.c index c6b0619681..a522fe93bb 100644 --- a/src/edit.c +++ b/src/edit.c @@ -4653,7 +4653,7 @@ ins_compl_get_exp(pos_T *ini) found_new_match = search_for_exact_line(ins_buf, pos, compl_direction, compl_pattern); else - found_new_match = searchit(NULL, ins_buf, pos, + found_new_match = searchit(NULL, ins_buf, pos, NULL, compl_direction, compl_pattern, 1L, SEARCH_KEEP + SEARCH_NFMSG, RE_LAST, (linenr_T)0, NULL, NULL); diff --git a/src/evalfunc.c b/src/evalfunc.c index bd2acef7fd..99e68f7e50 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -10056,7 +10056,7 @@ search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) } pos = save_cursor = curwin->w_cursor; - subpatnum = searchit(curwin, curbuf, &pos, dir, pat, 1L, + subpatnum = searchit(curwin, curbuf, &pos, NULL, dir, pat, 1L, options, RE_SEARCH, (linenr_T)lnum_stop, &tm, NULL); if (subpatnum != FAIL) { @@ -10414,7 +10414,7 @@ do_searchpair( pat = pat3; for (;;) { - n = searchit(curwin, curbuf, &pos, dir, pat, 1L, + n = searchit(curwin, curbuf, &pos, NULL, dir, pat, 1L, options, RE_SEARCH, lnum_stop, &tm, NULL); if (n == FAIL || (firstpos.lnum != 0 && EQUAL_POS(pos, firstpos))) /* didn't find it or found the first match again: FAIL */ diff --git a/src/ex_docmd.c b/src/ex_docmd.c index d3246f439b..67f505d960 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -4669,7 +4669,7 @@ get_address( #ifdef FEAT_VIRTUALEDIT pos.coladd = 0; #endif - if (searchit(curwin, curbuf, &pos, + if (searchit(curwin, curbuf, &pos, NULL, *cmd == '?' ? BACKWARD : FORWARD, (char_u *)"", 1L, SEARCH_MSG, i, (linenr_T)0, NULL, NULL) != FAIL) diff --git a/src/ex_getln.c b/src/ex_getln.c index 4ea8bfa9d3..4ad080aa2f 100644 --- a/src/ex_getln.c +++ b/src/ex_getln.c @@ -675,7 +675,7 @@ may_adjust_incsearch_highlighting( ++emsg_off; save = pat[patlen]; pat[patlen] = NUL; - i = searchit(curwin, curbuf, &t, + i = searchit(curwin, curbuf, &t, NULL, c == Ctrl_G ? FORWARD : BACKWARD, pat, count, search_flags, RE_SEARCH, 0, NULL, NULL); diff --git a/src/normal.c b/src/normal.c index 5310824eee..78e3f201cb 100644 --- a/src/normal.c +++ b/src/normal.c @@ -4338,7 +4338,7 @@ find_decl( for (;;) { valid = FALSE; - t = searchit(curwin, curbuf, &curwin->w_cursor, FORWARD, + t = searchit(curwin, curbuf, &curwin->w_cursor, NULL, FORWARD, pat, 1L, searchflags, RE_LAST, (linenr_T)0, NULL, NULL); if (curwin->w_cursor.lnum >= old_pos.lnum) t = FAIL; /* match after start is failure too */ diff --git a/src/proto/search.pro b/src/proto/search.pro index eeaa37cdd8..eb614a18fe 100644 --- a/src/proto/search.pro +++ b/src/proto/search.pro @@ -22,7 +22,7 @@ char_u *last_search_pat(void); void reset_search_dir(void); void set_last_search_pat(char_u *s, int idx, int magic, int setlast); void last_pat_prog(regmmatch_T *regmatch); -int searchit(win_T *win, buf_T *buf, pos_T *pos, int dir, char_u *pat, long count, int options, int pat_use, linenr_T stop_lnum, proftime_T *tm, int *timed_out); +int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, int dir, char_u *pat, long count, int options, int pat_use, linenr_T stop_lnum, proftime_T *tm, int *timed_out); void set_search_direction(int cdir); int do_search(oparg_T *oap, int dirc, char_u *pat, long count, int options, proftime_T *tm, int *timed_out); int search_for_exact_line(buf_T *buf, pos_T *pos, int dir, char_u *pat); diff --git a/src/search.c b/src/search.c index 0cbbf4fefb..8e8e6e4f19 100644 --- a/src/search.c +++ b/src/search.c @@ -610,8 +610,8 @@ last_pat_prog(regmmatch_T *regmatch) /* * Lowest level search function. - * Search for 'count'th occurrence of pattern 'pat' in direction 'dir'. - * Start at position 'pos' and return the found position in 'pos'. + * Search for 'count'th occurrence of pattern "pat" in direction "dir". + * Start at position "pos" and return the found position in "pos". * * if (options & SEARCH_MSG) == 0 don't give any messages * if (options & SEARCH_MSG) == SEARCH_NFMSG don't give 'notfound' messages @@ -634,6 +634,7 @@ searchit( buffer without a window! */ buf_T *buf, pos_T *pos, + pos_T *end_pos, // set to end of the match, unless NULL int dir, char_u *pat, long count, @@ -1035,14 +1036,26 @@ searchit( } #endif } + if (end_pos != NULL) + { + end_pos->lnum = lnum + matchpos.lnum; + end_pos->col = matchpos.col; + } } else { pos->lnum = lnum + matchpos.lnum; pos->col = matchpos.col; + if (end_pos != NULL) + { + end_pos->lnum = lnum + endpos.lnum; + end_pos->col = endpos.col; + } } #ifdef FEAT_VIRTUALEDIT pos->coladd = 0; + if (end_pos != NULL) + end_pos->coladd = 0; #endif found = 1; first_match = FALSE; @@ -1496,7 +1509,7 @@ do_search( lrFswap(searchstr,0); #endif - c = searchit(curwin, curbuf, &pos, dirc == '/' ? FORWARD : BACKWARD, + c = searchit(curwin, curbuf, &pos, NULL, dirc == '/' ? FORWARD : BACKWARD, searchstr, count, spats[0].off.end + (options & (SEARCH_KEEP + SEARCH_PEEK + SEARCH_HIS + SEARCH_MSG + SEARCH_START @@ -4665,20 +4678,19 @@ static int is_one_char(char_u *pattern, int move, pos_T *cur, int direction); int current_search( long count, - int forward) /* move forward or backwards */ + int forward) // TRUE for forward, FALSE for backward { - pos_T start_pos; /* position before the pattern */ - pos_T orig_pos; /* position of the cursor at beginning */ - pos_T first_match; /* position of first match */ - pos_T pos; /* position after the pattern */ + pos_T start_pos; // start position of the pattern match + pos_T end_pos; // end position of the pattern match + pos_T orig_pos; // position of the cursor at beginning + pos_T pos; // position after the pattern int i; int dir; - int result; /* result of various function calls */ + int result; // result of various function calls char_u old_p_ws = p_ws; int flags = 0; pos_T save_VIsual = VIsual; int one_char; - int direction = forward ? FORWARD : BACKWARD; /* wrapping should not occur */ p_ws = FALSE; @@ -4730,8 +4742,10 @@ current_search( flags = 0; if (!dir && !one_char) flags = SEARCH_END; + end_pos = pos; - result = searchit(curwin, curbuf, &pos, (dir ? FORWARD : BACKWARD), + result = searchit(curwin, curbuf, &pos, &end_pos, + (dir ? FORWARD : BACKWARD), spats[last_idx].pat, (long) (i ? count : 1), SEARCH_KEEP | flags, RE_SEARCH, 0, NULL, NULL); @@ -4739,7 +4753,7 @@ current_search( * beginning of the file (cursor might be on the search match) * except when Visual mode is active, so that extending the visual * selection works. */ - if (!result && i) /* not found, abort */ + if (i == 1 && !result) /* not found, abort */ { curwin->w_cursor = orig_pos; if (VIsual_active) @@ -4747,7 +4761,7 @@ current_search( p_ws = old_p_ws; return FAIL; } - else if (!i && !result) + else if (i == 0 && !result) { if (forward) { @@ -4763,48 +4777,19 @@ current_search( ml_get(curwin->w_buffer->b_ml.ml_line_count)); } } - if (i == 0) - first_match = pos; p_ws = old_p_ws; } start_pos = pos; - flags = forward ? SEARCH_END : SEARCH_START; - - /* Check again from the current cursor position, - * since the next match might actually by only one char wide */ - one_char = is_one_char(spats[last_idx].pat, FALSE, &pos, direction); - if (one_char < 0) - /* search failed, abort */ - return FAIL; - - /* move to match, except for zero-width matches, in which case, we are - * already on the next match */ - if (!one_char) - { - p_ws = FALSE; - for (i = 0; i < 2; i++) - { - result = searchit(curwin, curbuf, &pos, direction, - spats[last_idx].pat, 0L, flags | SEARCH_KEEP, RE_SEARCH, 0, - NULL, NULL); - /* Search successfull, break out from the loop */ - if (result) - break; - /* search failed, try again from the last search position match */ - pos = first_match; - } - } - p_ws = old_p_ws; - /* not found */ - if (!result) - return FAIL; if (!VIsual_active) VIsual = start_pos; - curwin->w_cursor = pos; + // put cursor on last character of match + curwin->w_cursor = end_pos; + if (LT_POS(VIsual, end_pos)) + dec_cursor(); VIsual_active = TRUE; VIsual_mode = 'v'; @@ -4880,7 +4865,7 @@ is_one_char(char_u *pattern, int move, pos_T *cur, int direction) flag = SEARCH_START; } - if (searchit(curwin, curbuf, &pos, direction, pattern, 1, + if (searchit(curwin, curbuf, &pos, NULL, direction, pattern, 1, SEARCH_KEEP + flag, RE_SEARCH, 0, NULL, NULL) != FAIL) { /* Zero-width pattern should match somewhere, then we can check if diff --git a/src/testdir/test_gn.vim b/src/testdir/test_gn.vim index 6d48915c15..d0fdafcaaf 100644 --- a/src/testdir/test_gn.vim +++ b/src/testdir/test_gn.vim @@ -131,4 +131,23 @@ func Test_gn_command() set wrapscan&vim endfu +func Test_gn_multi_line() + new + call setline(1, [ + \ 'func Tm1()', + \ ' echo "one"', + \ 'endfunc', + \ 'func Tm2()', + \ ' echo "two"', + \ 'endfunc', + \ 'func Tm3()', + \ ' echo "three"', + \ 'endfunc', + \]) + /\v^func Tm\d\(\)\n.*\zs".*"\ze$ + normal jgnrx + call assert_equal(' echo xxxxx', getline(5)) + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index ff22c6d502..2155834dbf 100644 --- a/src/version.c +++ b/src/version.c @@ -799,6 +799,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 629, /**/ 628, /**/ From a42df5934bdc1178ed2ee8cb9c8686975b578497 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 24 Dec 2018 00:22:39 +0100 Subject: [PATCH 13/22] patch 8.1.0630: "wincmd p" does not work after using an autocmd window Problem: "wincmd p" does not work after using an autocmd window. Solution: Store "prevwin" in aco_save_T. (Christian Brabandt, closes #3690) --- src/fileio.c | 5 +++++ src/structs.h | 1 + src/testdir/test_window_cmd.vim | 37 +++++++++++++++++++++++++++++++++ src/version.c | 2 ++ 4 files changed, 45 insertions(+) diff --git a/src/fileio.c b/src/fileio.c index bf46522b92..fd8fd243fd 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -9017,6 +9017,7 @@ aucmd_prepbuf( aco->save_curwin = curwin; aco->save_curbuf = curbuf; + aco->save_prevwin = prevwin; if (win != NULL) { /* There is a window for "buf" in the current tab page, make it the @@ -9127,6 +9128,8 @@ win_found: else /* Hmm, original window disappeared. Just use the first one. */ curwin = firstwin; + if (win_valid(aco->save_prevwin)) + prevwin = aco->save_prevwin; #ifdef FEAT_EVAL vars_clear(&aucmd_win->w_vars->dv_hashtab); /* free all w: variables */ hash_init(&aucmd_win->w_vars->dv_hashtab); /* re-use the hashtab */ @@ -9177,6 +9180,8 @@ win_found: curwin = aco->save_curwin; curbuf = curwin->w_buffer; + if (win_valid(aco->save_prevwin)) + prevwin = aco->save_prevwin; /* In case the autocommand move the cursor to a position that that * not exist in curbuf. */ check_cursor(); diff --git a/src/structs.h b/src/structs.h index 09a566571a..0f37b8f66b 100644 --- a/src/structs.h +++ b/src/structs.h @@ -3252,6 +3252,7 @@ typedef struct int use_aucmd_win; /* using aucmd_win */ win_T *save_curwin; /* saved curwin */ win_T *new_curwin; /* new curwin */ + win_T *save_prevwin; /* saved prevwin */ bufref_T new_curbuf; /* new curbuf */ char_u *globaldir; /* saved value of globaldir */ } aco_save_T; diff --git a/src/testdir/test_window_cmd.vim b/src/testdir/test_window_cmd.vim index 6d65e2b29c..0980493fe8 100644 --- a/src/testdir/test_window_cmd.vim +++ b/src/testdir/test_window_cmd.vim @@ -578,4 +578,41 @@ func Test_winrestcmd() only endfunc +function! Fun_RenewFile() + sleep 2 + silent execute '!echo "1" > tmp.txt' + sp + wincmd p + edit! tmp.txt +endfunction + +func Test_window_prevwin() + " Can we make this work on MS-Windows? + if !has('unix') + return + endif + + set hidden autoread + call writefile(['2'], 'tmp.txt') + new tmp.txt + q + " Need to wait a bit for the timestamp to be older. + call Fun_RenewFile() + call assert_equal(2, winnr()) + wincmd p + call assert_equal(1, winnr()) + wincmd p + q + call Fun_RenewFile() + call assert_equal(2, winnr()) + wincmd p + call assert_equal(1, winnr()) + wincmd p + " reset + q + call delete('tmp.txt') + set hidden&vim autoread&vim + delfunc Fun_RenewFile +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 2155834dbf..1b9e3e0dcb 100644 --- a/src/version.c +++ b/src/version.c @@ -799,6 +799,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 630, /**/ 629, /**/ From d6024e2dd4e0c1556d9b69e61c4059fa78e5609d Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 24 Dec 2018 19:15:20 +0100 Subject: [PATCH 14/22] patch 8.1.0631: test for :stop fails on Arch Problem: Test for :stop fails on Arch. Solution: Check five lines for the expected output. (closes #3714) --- src/testdir/test_terminal.vim | 6 ++++-- src/version.c | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/testdir/test_terminal.vim b/src/testdir/test_terminal.vim index f31c221e1f..71cf1b2c92 100644 --- a/src/testdir/test_terminal.vim +++ b/src/testdir/test_terminal.vim @@ -1705,11 +1705,13 @@ func Test_stop_in_terminal() call WaitForAssert({-> assert_match('ready', Get_terminal_text(bufnr, lastrow))}) call term_sendkeys(bufnr, ":stop\r") - " Not sure where "Stopped" shows up, assume in the first three lines. + " Not sure where "Stopped" shows up, need five lines for Arch. call WaitForAssert({-> assert_match('Stopped', \ Get_terminal_text(bufnr, 1) . \ Get_terminal_text(bufnr, 2) . - \ Get_terminal_text(bufnr, 3))}) + \ Get_terminal_text(bufnr, 3) . + \ Get_terminal_text(bufnr, 4) . + \ Get_terminal_text(bufnr, 5))}) call term_sendkeys(bufnr, "fg\r") call term_sendkeys(bufnr, ":echo 'back again'\r") diff --git a/src/version.c b/src/version.c index 1b9e3e0dcb..d53ffac0e6 100644 --- a/src/version.c +++ b/src/version.c @@ -799,6 +799,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 631, /**/ 630, /**/ From 7a2d9892b7158edf8dc48e9bcaaae70a40787b37 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 24 Dec 2018 20:23:49 +0100 Subject: [PATCH 15/22] patch 8.1.0632: using sign group names is inefficient Problem: Using sign group names is inefficient. Solution: Store group names in a hash table and use a reference to them. Also remove unnecessary use of ":exe" from the tests. (Yegappan Lakshmanan, closes #3715) --- src/buffer.c | 101 ++++++++++++++++++++++++++---- src/ex_cmds.c | 2 +- src/structs.h | 13 +++- src/testdir/test_signs.vim | 124 +++++++++++++++++++------------------ src/version.c | 2 + 5 files changed, 170 insertions(+), 72 deletions(-) diff --git a/src/buffer.c b/src/buffer.c index 4055af904f..0ac76b0636 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -5865,6 +5865,73 @@ win_found: #endif #if defined(FEAT_SIGNS) || defined(PROTO) +static hashtab_T sg_table; // sign group (signgroup_T) hashtable + +/* + * A new sign in group 'groupname' is added. If the group is not present, + * create it. Otherwise reference the group. + */ + static signgroup_T * +sign_group_ref(char_u *groupname) +{ + static int initialized = FALSE; + hash_T hash; + hashitem_T *hi; + signgroup_T *group; + + if (!initialized) + { + initialized = TRUE; + hash_init(&sg_table); + } + + hash = hash_hash(groupname); + hi = hash_lookup(&sg_table, groupname, hash); + if (HASHITEM_EMPTY(hi)) + { + // new group + group = (signgroup_T *)alloc( + (unsigned)(sizeof(signgroup_T) + STRLEN(groupname))); + if (group == NULL) + return NULL; + STRCPY(group->sg_name, groupname); + group->refcount = 1; + hash_add_item(&sg_table, hi, group->sg_name, hash); + } + else + { + // existing group + group = HI2SG(hi); + group->refcount++; + } + + return group; +} + +/* + * A sign in group 'groupname' is removed. If all the signs in this group are + * removed, then remove the group. + */ + static void +sign_group_unref(char_u *groupname) +{ + hashitem_T *hi; + signgroup_T *group; + + hi = hash_find(&sg_table, groupname); + if (!HASHITEM_EMPTY(hi)) + { + group = HI2SG(hi); + group->refcount--; + if (group->refcount == 0) + { + // All the signs in this group are removed + hash_remove(&sg_table, hi); + vim_free(group); + } + } +} + /* * Insert a new sign into the signlist for buffer 'buf' between the 'prev' and * 'next' signs. @@ -5890,7 +5957,14 @@ insert_sign( newsign->lnum = lnum; newsign->typenr = typenr; if (group != NULL) - newsign->group = vim_strsave(group); + { + newsign->group = sign_group_ref(group); + if (newsign->group == NULL) + { + vim_free(newsign); + return; + } + } else newsign->group = NULL; newsign->priority = prio; @@ -5959,7 +6033,7 @@ sign_in_group(signlist_T *sign, char_u *group) return ((group != NULL && STRCMP(group, "*") == 0) || (group == NULL && sign->group == NULL) || (group != NULL && sign->group != NULL && - STRCMP(group, sign->group) == 0)); + STRCMP(group, sign->group->sg_name) == 0)); } /* @@ -5974,7 +6048,7 @@ sign_get_info(signlist_T *sign) return NULL; dict_add_number(d, "id", sign->id); dict_add_string(d, "group", (sign->group == NULL) ? - (char_u *)"" : sign->group); + (char_u *)"" : sign->group->sg_name); dict_add_number(d, "lnum", sign->lnum); dict_add_string(d, "name", sign_typenr2name(sign->typenr)); dict_add_number(d, "priority", sign->priority); @@ -5989,7 +6063,7 @@ sign_get_info(signlist_T *sign) buf_addsign( buf_T *buf, // buffer to store sign in int id, // sign ID - char_u *group, // sign group + char_u *groupname, // sign group int prio, // sign priority linenr_T lnum, // line number which gets the mark int typenr) // typenr of sign we are adding @@ -6001,7 +6075,7 @@ buf_addsign( FOR_ALL_SIGNS_IN_BUF(buf) { if (lnum == sign->lnum && id == sign->id && - sign_in_group(sign, group)) + sign_in_group(sign, groupname)) { // Update an existing sign sign->typenr = typenr; @@ -6009,14 +6083,14 @@ buf_addsign( } else if (lnum < sign->lnum) { - insert_sign_by_lnum_prio(buf, prev, id, group, prio, lnum, typenr); + insert_sign_by_lnum_prio(buf, prev, id, groupname, prio, + lnum, typenr); return; } prev = sign; } - insert_sign_by_lnum_prio(buf, prev, id, group, prio, lnum, typenr); - + insert_sign_by_lnum_prio(buf, prev, id, groupname, prio, lnum, typenr); return; } @@ -6106,7 +6180,8 @@ buf_delsign( if (next != NULL) next->prev = sign->prev; lnum = sign->lnum; - vim_free(sign->group); + if (sign->group != NULL) + sign_group_unref(sign->group->sg_name); vim_free(sign); // Check whether only one sign needs to be deleted if (group == NULL || (*group != '*' && id != 0)) @@ -6269,7 +6344,8 @@ buf_delete_signs(buf_T *buf, char_u *group) *lastp = next; if (next != NULL) next->prev = sign->prev; - vim_free(sign->group); + if (sign->group != NULL) + sign_group_unref(sign->group->sg_name); vim_free(sign); } else @@ -6317,10 +6393,13 @@ sign_list_placed(buf_T *rbuf, char_u *sign_group) } FOR_ALL_SIGNS_IN_BUF(buf) { + if (got_int) + break; if (!sign_in_group(sign, sign_group)) continue; if (sign->group != NULL) - vim_snprintf(group, BUFSIZ, " group=%s", sign->group); + vim_snprintf(group, BUFSIZ, " group=%s", + sign->group->sg_name); else group[0] = '\0'; vim_snprintf(lbuf, BUFSIZ, _(" line=%ld id=%d%s name=%s " diff --git a/src/ex_cmds.c b/src/ex_cmds.c index 06df5affe2..52b669ae94 100644 --- a/src/ex_cmds.c +++ b/src/ex_cmds.c @@ -8180,7 +8180,7 @@ ex_sign(exarg_T *eap) { EMSG2(_("E158: Invalid buffer name: %s"), arg); } - else if (id <= 0 && !(idx == SIGNCMD_UNPLACE && id == -2)) + else if (id <= 0 && idx == SIGNCMD_PLACE) { if ((group == NULL) && (lnum >= 0 || sign_name != NULL)) EMSG(_(e_invarg)); diff --git a/src/structs.h b/src/structs.h index 0f37b8f66b..e4311eda41 100644 --- a/src/structs.h +++ b/src/structs.h @@ -733,6 +733,17 @@ typedef struct proptype_S #if defined(FEAT_SIGNS) || defined(PROTO) +// Sign group +typedef struct signgroup_S +{ + short_u refcount; // number of signs in this group + char_u sg_name[1]; // sign group name +} signgroup_T; + +// Macros to get the sign group structure from the group name +#define SGN_KEY_OFF offsetof(signgroup_T, sg_name) +#define HI2SG(hi) ((signgroup_T *)((hi)->hi_key - SGN_KEY_OFF)) + typedef struct signlist signlist_T; struct signlist @@ -740,7 +751,7 @@ struct signlist int id; /* unique identifier for each placed sign */ linenr_T lnum; /* line number which has this sign */ int typenr; /* typenr of sign */ - char_u *group; /* sign group */ + signgroup_T *group; /* sign group */ int priority; /* priority for highlighting */ signlist_T *next; /* next signlist entry */ signlist_T *prev; /* previous entry -- for easy reordering */ diff --git a/src/testdir/test_signs.vim b/src/testdir/test_signs.vim index 07705c7bb6..0f589c5160 100644 --- a/src/testdir/test_signs.vim +++ b/src/testdir/test_signs.vim @@ -59,7 +59,7 @@ func Test_sign() redraw " Check that we can't change sign. - call assert_fails("exe 'sign place 40 name=Sign1 buffer=' . bufnr('%')", 'E885:') + call assert_fails("sign place 40 name=Sign1 buffer=" . bufnr('%'), 'E885:') " Check placed signs let a=execute('sign place') @@ -68,7 +68,7 @@ func Test_sign() " Unplace the sign and try jumping to it again should fail. sign unplace 41 1 - call assert_fails("exe 'sign jump 41 buffer=' . bufnr('%')", 'E157:') + call assert_fails("sign jump 41 buffer=" . bufnr('%'), 'E157:') call assert_equal('a', getline('.')) " Unplace sign on current line. @@ -132,17 +132,22 @@ func Test_sign() sign undefine Sign4 " Error cases - call assert_fails("exe 'sign place abc line=3 name=Sign1 buffer=' . bufnr('%')", 'E474:') - call assert_fails("exe 'sign unplace abc name=Sign1 buffer=' . bufnr('%')", 'E474:') - call assert_fails("exe 'sign place 1abc line=3 name=Sign1 buffer=' . bufnr('%')", 'E474:') - call assert_fails("exe 'sign unplace 2abc name=Sign1 buffer=' . bufnr('%')", 'E474:') + call assert_fails("sign place abc line=3 name=Sign1 buffer=" . + \ bufnr('%'), 'E474:') + call assert_fails("sign unplace abc name=Sign1 buffer=" . + \ bufnr('%'), 'E474:') + call assert_fails("sign place 1abc line=3 name=Sign1 buffer=" . + \ bufnr('%'), 'E474:') + call assert_fails("sign unplace 2abc name=Sign1 buffer=" . + \ bufnr('%'), 'E474:') call assert_fails("sign unplace 2 *", 'E474:') - call assert_fails("exe 'sign place 1 line=3 name=Sign1 buffer=' . bufnr('%') a", 'E488:') - call assert_fails("exe 'sign place name=Sign1 buffer=' . bufnr('%')", 'E474:') - call assert_fails("exe 'sign place line=10 buffer=' . bufnr('%')", 'E474:') - call assert_fails("exe 'sign unplace 2 line=10 buffer=' . bufnr('%')", 'E474:') - call assert_fails("exe 'sign unplace 2 name=Sign1 buffer=' . bufnr('%')", 'E474:') - call assert_fails("exe 'sign place 2 line=3 buffer=' . bufnr('%')", 'E474:') + call assert_fails("sign place 1 line=3 name=Sign1 buffer=" . + \ bufnr('%') . " a", 'E488:') + call assert_fails("sign place name=Sign1 buffer=" . bufnr('%'), 'E474:') + call assert_fails("sign place line=10 buffer=" . bufnr('%'), 'E474:') + call assert_fails("sign unplace 2 line=10 buffer=" . bufnr('%'), 'E474:') + call assert_fails("sign unplace 2 name=Sign1 buffer=" . bufnr('%'), 'E474:') + call assert_fails("sign place 2 line=3 buffer=" . bufnr('%'), 'E474:') call assert_fails("sign place 2", 'E474:') call assert_fails("sign place abc", 'E474:') call assert_fails("sign place 5 line=3", 'E474:') @@ -157,7 +162,8 @@ func Test_sign() sign undefine Sign1 sign undefine Sign2 sign undefine Sign3 - call assert_fails("exe 'sign place 41 line=3 name=Sign1 buffer=' . bufnr('%')", 'E155:') + call assert_fails("sign place 41 line=3 name=Sign1 buffer=" . + \ bufnr('%'), 'E155:') endfunc " Undefining placed sign is not recommended. @@ -236,33 +242,33 @@ func Test_sign_invalid_commands() call assert_fails('sign place 1 buffer=999', 'E158:') call assert_fails('sign define Sign2 text=', 'E239:') " Non-numeric identifier for :sign place - call assert_fails("exe 'sign place abc line=3 name=Sign1 buffer=' . bufnr('%')", 'E474:') + call assert_fails("sign place abc line=3 name=Sign1 buffer=" . bufnr('%'), 'E474:') " Non-numeric identifier for :sign unplace - call assert_fails("exe 'sign unplace abc name=Sign1 buffer=' . bufnr('%')", 'E474:') + call assert_fails("sign unplace abc name=Sign1 buffer=" . bufnr('%'), 'E474:') " Number followed by an alphabet as sign identifier for :sign place - call assert_fails("exe 'sign place 1abc line=3 name=Sign1 buffer=' . bufnr('%')", 'E474:') + call assert_fails("sign place 1abc line=3 name=Sign1 buffer=" . bufnr('%'), 'E474:') " Number followed by an alphabet as sign identifier for :sign unplace - call assert_fails("exe 'sign unplace 2abc name=Sign1 buffer=' . bufnr('%')", 'E474:') + call assert_fails("sign unplace 2abc name=Sign1 buffer=" . bufnr('%'), 'E474:') " Sign identifier and '*' for :sign unplace call assert_fails("sign unplace 2 *", 'E474:') " Trailing characters after buffer number for :sign place - call assert_fails("exe 'sign place 1 line=3 name=Sign1 buffer=' . bufnr('%') . 'xxx'", 'E488:') + call assert_fails("sign place 1 line=3 name=Sign1 buffer=" . bufnr('%') . 'xxx', 'E488:') " Trailing characters after buffer number for :sign unplace - call assert_fails("exe 'sign unplace 1 buffer=' . bufnr('%') . 'xxx'", 'E488:') - call assert_fails("exe 'sign unplace * buffer=' . bufnr('%') . 'xxx'", 'E488:') + call assert_fails("sign unplace 1 buffer=" . bufnr('%') . 'xxx', 'E488:') + call assert_fails("sign unplace * buffer=" . bufnr('%') . 'xxx', 'E488:') call assert_fails("sign unplace 1 xxx", 'E474:') call assert_fails("sign unplace * xxx", 'E474:') call assert_fails("sign unplace xxx", 'E474:') " Placing a sign without line number - call assert_fails("exe 'sign place name=Sign1 buffer=' . bufnr('%')", 'E474:') + call assert_fails("sign place name=Sign1 buffer=" . bufnr('%'), 'E474:') " Placing a sign without sign name - call assert_fails("exe 'sign place line=10 buffer=' . bufnr('%')", 'E474:') + call assert_fails("sign place line=10 buffer=" . bufnr('%'), 'E474:') " Unplacing a sign with line number - call assert_fails("exe 'sign unplace 2 line=10 buffer=' . bufnr('%')", 'E474:') + call assert_fails("sign unplace 2 line=10 buffer=" . bufnr('%'), 'E474:') " Unplacing a sign with sign name - call assert_fails("exe 'sign unplace 2 name=Sign1 buffer=' . bufnr('%')", 'E474:') + call assert_fails("sign unplace 2 name=Sign1 buffer=" . bufnr('%'), 'E474:') " Placing a sign without sign name - call assert_fails("exe 'sign place 2 line=3 buffer=' . bufnr('%')", 'E474:') + call assert_fails("sign place 2 line=3 buffer=" . bufnr('%'), 'E474:') " Placing a sign with only sign identifier call assert_fails("sign place 2", 'E474:') " Placing a sign with only a name @@ -574,24 +580,24 @@ func Test_sign_group() call sign_unplace('*') " Test for :sign command and groups - exe 'sign place 5 line=10 name=sign1 file=' . fname - exe 'sign place 5 group=g1 line=10 name=sign1 file=' . fname - exe 'sign place 5 group=g2 line=10 name=sign1 file=' . fname + sign place 5 line=10 name=sign1 file=Xsign + sign place 5 group=g1 line=10 name=sign1 file=Xsign + sign place 5 group=g2 line=10 name=sign1 file=Xsign " Test for :sign place group={group} file={fname} - let a = execute('sign place file=' . fname) + let a = execute('sign place file=Xsign') call assert_equal("\n--- Signs ---\nSigns for Xsign:\n line=10 id=5 name=sign1 priority=10\n", a) - let a = execute('sign place group=g2 file=' . fname) + let a = execute('sign place group=g2 file=Xsign') call assert_equal("\n--- Signs ---\nSigns for Xsign:\n line=10 id=5 group=g2 name=sign1 priority=10\n", a) - let a = execute('sign place group=* file=' . fname) + let a = execute('sign place group=* file=Xsign') call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . \ " line=10 id=5 group=g2 name=sign1 priority=10\n" . \ " line=10 id=5 group=g1 name=sign1 priority=10\n" . \ " line=10 id=5 name=sign1 priority=10\n", a) - let a = execute('sign place group=xyz file=' . fname) + let a = execute('sign place group=xyz file=Xsign') call assert_equal("\n--- Signs ---\nSigns for Xsign:\n", a) call sign_unplace('*') @@ -624,22 +630,22 @@ func Test_sign_group() \ " line=12 id=5 group=g2 name=sign1 priority=10\n", a) " Test for :sign unplace - exe 'sign unplace 5 group=g2 file=' . fname + sign unplace 5 group=g2 file=Xsign call assert_equal([], sign_getplaced(bnum, {'group' : 'g2'})[0].signs) exe 'sign unplace 5 group=g1 buffer=' . bnum call assert_equal([], sign_getplaced(bnum, {'group' : 'g1'})[0].signs) - exe 'sign unplace 5 group=xy file=' . fname + sign unplace 5 group=xy file=Xsign call assert_equal(1, len(sign_getplaced(bnum, {'group' : '*'})[0].signs)) " Test for removing all the signs. Place the signs again for this test - exe 'sign place 5 group=g1 line=11 name=sign1 file=' . fname - exe 'sign place 5 group=g2 line=12 name=sign1 file=' . fname - exe 'sign place 6 line=20 name=sign1 file=' . fname - exe 'sign place 6 group=g1 line=21 name=sign1 file=' . fname - exe 'sign place 6 group=g2 line=22 name=sign1 file=' . fname - exe 'sign unplace 5 group=* file=' . fname + sign place 5 group=g1 line=11 name=sign1 file=Xsign + sign place 5 group=g2 line=12 name=sign1 file=Xsign + sign place 6 line=20 name=sign1 file=Xsign + sign place 6 group=g1 line=21 name=sign1 file=Xsign + sign place 6 group=g2 line=22 name=sign1 file=Xsign + sign unplace 5 group=* file=Xsign let a = execute('sign place group=*') call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . \ " line=20 id=6 name=sign1 priority=10\n" . @@ -647,17 +653,17 @@ func Test_sign_group() \ " line=22 id=6 group=g2 name=sign1 priority=10\n", a) " Remove all the signs from the global group - exe 'sign unplace * file=' . fname + sign unplace * file=Xsign let a = execute('sign place group=*') call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . \ " line=21 id=6 group=g1 name=sign1 priority=10\n" . \ " line=22 id=6 group=g2 name=sign1 priority=10\n", a) " Remove all the signs from a particular group - exe 'sign place 5 line=10 name=sign1 file=' . fname - exe 'sign place 5 group=g1 line=11 name=sign1 file=' . fname - exe 'sign place 5 group=g2 line=12 name=sign1 file=' . fname - exe 'sign unplace * group=g1 file=' . fname + sign place 5 line=10 name=sign1 file=Xsign + sign place 5 group=g1 line=11 name=sign1 file=Xsign + sign place 5 group=g2 line=12 name=sign1 file=Xsign + sign unplace * group=g1 file=Xsign let a = execute('sign place group=*') call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . \ " line=10 id=5 name=sign1 priority=10\n" . @@ -665,26 +671,26 @@ func Test_sign_group() \ " line=22 id=6 group=g2 name=sign1 priority=10\n", a) " Remove all the signs from all the groups in a file - exe 'sign place 5 group=g1 line=11 name=sign1 file=' . fname - exe 'sign place 6 line=20 name=sign1 file=' . fname - exe 'sign place 6 group=g1 line=21 name=sign1 file=' . fname - exe 'sign unplace * group=* file=' . fname + sign place 5 group=g1 line=11 name=sign1 file=Xsign + sign place 6 line=20 name=sign1 file=Xsign + sign place 6 group=g1 line=21 name=sign1 file=Xsign + sign unplace * group=* file=Xsign let a = execute('sign place group=*') call assert_equal("\n--- Signs ---\n", a) " Remove a particular sign id in a group from all the files - exe 'sign place 5 group=g1 line=11 name=sign1 file=' . fname + sign place 5 group=g1 line=11 name=sign1 file=Xsign sign unplace 5 group=g1 let a = execute('sign place group=*') call assert_equal("\n--- Signs ---\n", a) " Remove a particular sign id in all the groups from all the files - exe 'sign place 5 line=10 name=sign1 file=' . fname - exe 'sign place 5 group=g1 line=11 name=sign1 file=' . fname - exe 'sign place 5 group=g2 line=12 name=sign1 file=' . fname - exe 'sign place 6 line=20 name=sign1 file=' . fname - exe 'sign place 6 group=g1 line=21 name=sign1 file=' . fname - exe 'sign place 6 group=g2 line=22 name=sign1 file=' . fname + sign place 5 line=10 name=sign1 file=Xsign + sign place 5 group=g1 line=11 name=sign1 file=Xsign + sign place 5 group=g2 line=12 name=sign1 file=Xsign + sign place 6 line=20 name=sign1 file=Xsign + sign place 6 group=g1 line=21 name=sign1 file=Xsign + sign place 6 group=g2 line=22 name=sign1 file=Xsign sign unplace 5 group=* let a = execute('sign place group=*') call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . @@ -693,14 +699,14 @@ func Test_sign_group() \ " line=22 id=6 group=g2 name=sign1 priority=10\n", a) " Remove all the signs from all the groups in all the files - exe 'sign place 5 line=10 name=sign1 file=' . fname - exe 'sign place 5 group=g1 line=11 name=sign1 file=' . fname + sign place 5 line=10 name=sign1 file=Xsign + sign place 5 group=g1 line=11 name=sign1 file=Xsign sign unplace * group=* let a = execute('sign place group=*') call assert_equal("\n--- Signs ---\n", a) " Error cases - call assert_fails("exe 'sign place 3 group= name=sign1 buffer=' . bnum", 'E474:') + call assert_fails("sign place 3 group= name=sign1 buffer=" . bnum, 'E474:') call delete("Xsign") call sign_unplace('*') diff --git a/src/version.c b/src/version.c index d53ffac0e6..2452ae0463 100644 --- a/src/version.c +++ b/src/version.c @@ -799,6 +799,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 632, /**/ 631, /**/ From cd929f7ba8cc5b6d6dcf35c8b34124e969fed6b8 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 24 Dec 2018 21:38:45 +0100 Subject: [PATCH 16/22] patch 8.1.0633: crash when out of memory while opening a terminal window Problem: Crash when out of memory while opening a terminal window. Solution: Handle out-of-memory more gracefully. --- src/libvterm/src/state.c | 8 ++++++++ src/libvterm/src/termscreen.c | 28 ++++++++++++++++------------ src/libvterm/src/vterm.c | 20 +++++++++++++++++++- src/terminal.c | 32 +++++++++++++++++++++++++------- src/version.c | 2 ++ 5 files changed, 70 insertions(+), 20 deletions(-) diff --git a/src/libvterm/src/state.c b/src/libvterm/src/state.c index d977ecac60..8b02093bb9 100644 --- a/src/libvterm/src/state.c +++ b/src/libvterm/src/state.c @@ -53,6 +53,8 @@ static VTermState *vterm_state_new(VTerm *vt) { VTermState *state = vterm_allocator_malloc(vt, sizeof(VTermState)); + if (state == NULL) + return NULL; state->vt = vt; state->rows = vt->rows; @@ -1693,6 +1695,10 @@ static const VTermParserCallbacks parser_callbacks = { on_resize /* resize */ }; +/* + * Return the existing state or create a new one. + * Returns NULL when out of memory. + */ VTermState *vterm_obtain_state(VTerm *vt) { VTermState *state; @@ -1700,6 +1706,8 @@ VTermState *vterm_obtain_state(VTerm *vt) return vt->state; state = vterm_state_new(vt); + if (state == NULL) + return NULL; vt->state = state; state->combine_chars_size = 16; diff --git a/src/libvterm/src/termscreen.c b/src/libvterm/src/termscreen.c index cfae088ba7..0cd31cedfe 100644 --- a/src/libvterm/src/termscreen.c +++ b/src/libvterm/src/termscreen.c @@ -1,5 +1,6 @@ #include "vterm_internal.h" +/* vim: set sw=2 : */ #include #include @@ -95,8 +96,7 @@ static ScreenCell *realloc_buffer(VTermScreen *screen, ScreenCell *buffer, int n } } - if(buffer) - vterm_allocator_free(screen->vt, buffer); + vterm_allocator_free(screen->vt, buffer); return new_buffer; } @@ -518,8 +518,7 @@ static int resize(int new_rows, int new_cols, VTermPos *delta, void *user) screen->rows = new_rows; screen->cols = new_cols; - if(screen->sb_buffer) - vterm_allocator_free(screen->vt, screen->sb_buffer); + vterm_allocator_free(screen->vt, screen->sb_buffer); screen->sb_buffer = vterm_allocator_malloc(screen->vt, sizeof(VTermScreenCell) * new_cols); @@ -619,16 +618,21 @@ static VTermStateCallbacks state_cbs = { &setlineinfo /* setlineinfo */ }; +/* + * Allocate a new screen and return it. + * Return NULL when out of memory. + */ static VTermScreen *screen_new(VTerm *vt) { VTermState *state = vterm_obtain_state(vt); VTermScreen *screen; int rows, cols; - if(!state) + if (state == NULL) return NULL; - screen = vterm_allocator_malloc(vt, sizeof(VTermScreen)); + if (screen == NULL) + return NULL; vterm_get_size(vt, &rows, &cols); @@ -646,10 +650,13 @@ static VTermScreen *screen_new(VTerm *vt) screen->cbdata = NULL; screen->buffers[0] = realloc_buffer(screen, NULL, rows, cols); - screen->buffer = screen->buffers[0]; - screen->sb_buffer = vterm_allocator_malloc(screen->vt, sizeof(VTermScreenCell) * cols); + if (screen->buffer == NULL || screen->sb_buffer == NULL) + { + vterm_screen_free(screen); + return NULL; + } vterm_state_set_callbacks(screen->state, &state_cbs, screen); @@ -659,11 +666,8 @@ static VTermScreen *screen_new(VTerm *vt) INTERNAL void vterm_screen_free(VTermScreen *screen) { vterm_allocator_free(screen->vt, screen->buffers[0]); - if(screen->buffers[1]) - vterm_allocator_free(screen->vt, screen->buffers[1]); - + vterm_allocator_free(screen->vt, screen->buffers[1]); vterm_allocator_free(screen->vt, screen->sb_buffer); - vterm_allocator_free(screen->vt, screen); } diff --git a/src/libvterm/src/vterm.c b/src/libvterm/src/vterm.c index 9025da4449..5e4722ce3a 100644 --- a/src/libvterm/src/vterm.c +++ b/src/libvterm/src/vterm.c @@ -1,5 +1,6 @@ #define DEFINE_INLINES +/* vim: set sw=2 : */ #include "vterm_internal.h" #include @@ -41,6 +42,8 @@ VTerm *vterm_new_with_allocator(int rows, int cols, VTermAllocatorFunctions *fun /* Need to bootstrap using the allocator function directly */ VTerm *vt = (*funcs->malloc)(sizeof(VTerm), allocdata); + if (vt == NULL) + return NULL; vt->allocator = funcs; vt->allocdata = allocdata; @@ -55,10 +58,21 @@ VTerm *vterm_new_with_allocator(int rows, int cols, VTermAllocatorFunctions *fun vt->parser.strbuffer_len = 500; /* should be able to hold an OSC string */ vt->parser.strbuffer_cur = 0; vt->parser.strbuffer = vterm_allocator_malloc(vt, vt->parser.strbuffer_len); + if (vt->parser.strbuffer == NULL) + { + vterm_allocator_free(vt, vt); + return NULL; + } vt->outbuffer_len = 200; vt->outbuffer_cur = 0; vt->outbuffer = vterm_allocator_malloc(vt, vt->outbuffer_len); + if (vt->outbuffer == NULL) + { + vterm_allocator_free(vt, vt->parser.strbuffer); + vterm_allocator_free(vt, vt); + return NULL; + } return vt; } @@ -82,9 +96,13 @@ INTERNAL void *vterm_allocator_malloc(VTerm *vt, size_t size) return (*vt->allocator->malloc)(size, vt->allocdata); } +/* + * Free "ptr" unless it is NULL. + */ INTERNAL void vterm_allocator_free(VTerm *vt, void *ptr) { - (*vt->allocator->free)(ptr, vt->allocdata); + if (ptr) + (*vt->allocator->free)(ptr, vt->allocdata); } void vterm_get_size(const VTerm *vt, int *rowsp, int *colsp) diff --git a/src/terminal.c b/src/terminal.c index 06d470c560..1875cc3e34 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -3430,6 +3430,7 @@ set_vterm_palette(VTerm *vterm, long_u *rgb) { int index = 0; VTermState *state = vterm_obtain_state(vterm); + for (; index < 16; index++) { VTermColor color; @@ -3703,8 +3704,9 @@ static VTermAllocatorFunctions vterm_allocator = { /* * Create a new vterm and initialize it. + * Return FAIL when out of memory. */ - static void + static int create_vterm(term_T *term, int rows, int cols) { VTerm *vterm; @@ -3714,7 +3716,18 @@ create_vterm(term_T *term, int rows, int cols) vterm = vterm_new_with_allocator(rows, cols, &vterm_allocator, NULL); term->tl_vterm = vterm; + if (vterm == NULL) + return FAIL; + + // Allocate screen and state here, so we can bail out if that fails. + state = vterm_obtain_state(vterm); screen = vterm_obtain_screen(vterm); + if (state == NULL || screen == NULL) + { + vterm_free(vterm); + return FAIL; + } + vterm_screen_set_callbacks(screen, &screen_callbacks, term); /* TODO: depends on 'encoding'. */ vterm_set_utf8(vterm, 1); @@ -3722,7 +3735,7 @@ create_vterm(term_T *term, int rows, int cols) init_default_colors(term); vterm_state_set_default_colors( - vterm_obtain_state(vterm), + state, &term->tl_default_color.fg, &term->tl_default_color.bg); @@ -3746,9 +3759,10 @@ create_vterm(term_T *term, int rows, int cols) #else value.boolean = 0; #endif - state = vterm_obtain_state(vterm); vterm_state_set_termprop(state, VTERM_PROP_CURSORBLINK, &value); vterm_state_set_unrecognised_fallbacks(state, &parser_fallbacks, term); + + return OK; } /* @@ -5629,7 +5643,8 @@ term_and_job_init( vim_free(cwd_wchar); vim_free(env_wchar); - create_vterm(term, term->tl_rows, term->tl_cols); + if (create_vterm(term, term->tl_rows, term->tl_cols) == FAIL) + goto failed; #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) if (opt->jo_set2 & JO2_ANSI_COLORS) @@ -5710,7 +5725,8 @@ create_pty_only(term_T *term, jobopt_T *options) char in_name[80], out_name[80]; channel_T *channel = NULL; - create_vterm(term, term->tl_rows, term->tl_cols); + if (create_vterm(term, term->tl_rows, term->tl_cols) == FAIL) + return FAIL; vim_snprintf(in_name, sizeof(in_name), "\\\\.\\pipe\\vim-%d-in-%d", GetCurrentProcessId(), @@ -5822,7 +5838,8 @@ term_and_job_init( jobopt_T *opt, jobopt_T *orig_opt UNUSED) { - create_vterm(term, term->tl_rows, term->tl_cols); + if (create_vterm(term, term->tl_rows, term->tl_cols) == FAIL) + return FAIL; #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) if (opt->jo_set2 & JO2_ANSI_COLORS) @@ -5844,7 +5861,8 @@ term_and_job_init( static int create_pty_only(term_T *term, jobopt_T *opt) { - create_vterm(term, term->tl_rows, term->tl_cols); + if (create_vterm(term, term->tl_rows, term->tl_cols) == FAIL) + return FAIL; term->tl_job = job_alloc(); if (term->tl_job == NULL) diff --git a/src/version.c b/src/version.c index 2452ae0463..6956225c32 100644 --- a/src/version.c +++ b/src/version.c @@ -799,6 +799,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 633, /**/ 632, /**/ From e3d31b02a56710e64ef0c1eb6ac5afcc57cb4890 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 24 Dec 2018 23:07:04 +0100 Subject: [PATCH 17/22] patch 8.1.0634: text properties cannot cross line boundaries Problem: Text properties cannot cross line boundaries. Solution: Support multi-line text properties. --- runtime/doc/eval.txt | 10 +- src/testdir/test_textprop.vim | 30 ++++++ src/textprop.c | 174 ++++++++++++++++++++++------------ src/version.c | 2 + 4 files changed, 150 insertions(+), 66 deletions(-) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 2bc27a6295..cb1c61bedf 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1,4 +1,4 @@ -*eval.txt* For Vim version 8.1. Last change: 2018 Dec 18 +*eval.txt* For Vim version 8.1. Last change: 2018 Dec 24 VIM REFERENCE MANUAL by Bram Moolenaar @@ -2318,7 +2318,7 @@ prompt_setcallback({buf}, {expr}) none set prompt callback function prompt_setinterrupt({buf}, {text}) none set prompt interrupt function prompt_setprompt({buf}, {text}) none set prompt text prop_add({lnum}, {col}, {props}) none add a text property -prop_clear({lnum} [, {lnum-end} [, {bufnr}]]) +prop_clear({lnum} [, {lnum-end} [, {props}]]) none remove all text properties prop_find({props} [, {direction}]) Dict search for a text property @@ -6695,7 +6695,7 @@ prop_add({lnum}, {col}, {props}) used for a property that does not continue in another line "end_lnum" - line number for end of text - "end_col" - column for end of text; not used when + "end_col" - last column of the text; not used when "length" is present "bufnr" - buffer to add the property to; when omitted the current buffer is used @@ -6710,6 +6710,10 @@ prop_add({lnum}, {col}, {props}) property that spans more than one line. When neither "length" nor "end_col" are passed the property will apply to one character. + The property can end exactly at the last character of the + text, or just after it. In the last case, if text is appended + to the line, the text property size will increase, also when + the property type does not have "end_incl" set. "type" will first be looked up in the buffer the property is added to. When not found, the global property types are used. diff --git a/src/testdir/test_textprop.vim b/src/testdir/test_textprop.vim index 67038a27c4..081d4ab5f1 100644 --- a/src/testdir/test_textprop.vim +++ b/src/testdir/test_textprop.vim @@ -197,4 +197,34 @@ func Test_prop_clear_buf() bwipe! endfunc +func Test_prop_multiline() + call prop_type_add('comment', {'highlight': 'Directory'}) + new + call setline(1, ['xxxxxxx', 'yyyyyyyyy', 'zzzzzzzz']) + + " start halfway line 1, end halfway line 3 + call prop_add(1, 3, {'end_lnum': 3, 'end_col': 5, 'type': 'comment'}) + let expect1 = {'col': 3, 'length': 6, 'type': 'comment', 'start': 1, 'end': 0, 'id': 0} + call assert_equal([expect1], prop_list(1)) + let expect2 = {'col': 1, 'length': 10, 'type': 'comment', 'start': 0, 'end': 0, 'id': 0} + call assert_equal([expect2], prop_list(2)) + let expect3 = {'col': 1, 'length': 5, 'type': 'comment', 'start': 0, 'end': 1, 'id': 0} + call assert_equal([expect3], prop_list(3)) + call prop_clear(1, 3) + + " include all three lines + call prop_add(1, 1, {'end_lnum': 3, 'end_col': 999, 'type': 'comment'}) + let expect1.col = 1 + let expect1.length = 8 + call assert_equal([expect1], prop_list(1)) + call assert_equal([expect2], prop_list(2)) + let expect3.length = 9 + call assert_equal([expect3], prop_list(3)) + call prop_clear(1, 3) + + bwipe! + call prop_type_delete('comment') +endfunc + + " TODO: screenshot test with highlighting diff --git a/src/textprop.c b/src/textprop.c index ec9a87d51f..f5b977a7a0 100644 --- a/src/textprop.c +++ b/src/textprop.c @@ -17,10 +17,12 @@ * Text properties have a type, which can be used to specify highlighting. * * TODO: + * - When deleting a line where a prop ended, adjust flag of previous line. + * - When deleting a line where a prop started, adjust flag of next line. + * - When inserting a line add props that continue from previous line. + * - Adjust property column and length when text is inserted/deleted * - Add an arrray for global_proptypes, to quickly lookup a proptype by ID * - Add an arrray for b_proptypes, to quickly lookup a proptype by ID - * - adjust property column when text is inserted/deleted - * - support properties that continue over a line break * - add mechanism to keep track of changed lines. */ @@ -47,6 +49,7 @@ static int proptype_id = 0; static char_u e_type_not_exist[] = N_("E971: Property type %s does not exist"); static char_u e_invalid_col[] = N_("E964: Invalid column number: %ld"); +static char_u e_invalid_lnum[] = N_("E966: Invalid line number: %ld"); /* * Find a property type by name, return the hashitem. @@ -139,9 +142,11 @@ get_bufnr_from_arg(typval_T *arg, buf_T **buf) f_prop_add(typval_T *argvars, typval_T *rettv UNUSED) { linenr_T lnum; - colnr_T col; + linenr_T start_lnum; + linenr_T end_lnum; + colnr_T start_col; + colnr_T end_col; dict_T *dict; - colnr_T length = 1; char_u *type_name; proptype_T *type; buf_T *buf = curbuf; @@ -154,11 +159,11 @@ f_prop_add(typval_T *argvars, typval_T *rettv UNUSED) textprop_T tmp_prop; int i; - lnum = tv_get_number(&argvars[0]); - col = tv_get_number(&argvars[1]); - if (col < 1) + start_lnum = tv_get_number(&argvars[0]); + start_col = tv_get_number(&argvars[1]); + if (start_col < 1) { - EMSGN(_(e_invalid_col), (long)col); + EMSGN(_(e_invalid_col), (long)start_col); return; } if (argvars[2].v_type != VAR_DICT) @@ -177,22 +182,40 @@ f_prop_add(typval_T *argvars, typval_T *rettv UNUSED) if (dict_find(dict, (char_u *)"end_lnum", -1) != NULL) { - // TODO: handle end_lnum - EMSG("Sorry, end_lnum not supported yet"); - return; + end_lnum = dict_get_number(dict, (char_u *)"end_lnum"); + if (end_lnum < start_lnum) + { + EMSG2(_(e_invargval), "end_lnum"); + return; + } } + else + end_lnum = start_lnum; if (dict_find(dict, (char_u *)"length", -1) != NULL) - length = dict_get_number(dict, (char_u *)"length"); + { + long length = dict_get_number(dict, (char_u *)"length"); + + if (length < 1 || end_lnum > start_lnum) + { + EMSG2(_(e_invargval), "length"); + return; + } + end_col = start_col + length - 1; + } else if (dict_find(dict, (char_u *)"end_col", -1) != NULL) { - length = dict_get_number(dict, (char_u *)"end_col") - col; - if (length <= 0) + end_col = dict_get_number(dict, (char_u *)"end_col"); + if (end_col <= 0) { EMSG2(_(e_invargval), "end_col"); return; } } + else if (start_lnum == end_lnum) + end_col = start_col; + else + end_col = 1; if (dict_find(dict, (char_u *)"id", -1) != NULL) id = dict_get_number(dict, (char_u *)"id"); @@ -204,62 +227,87 @@ f_prop_add(typval_T *argvars, typval_T *rettv UNUSED) if (type == NULL) return; - if (lnum < 1 || lnum > buf->b_ml.ml_line_count) + if (start_lnum < 1 || start_lnum > buf->b_ml.ml_line_count) { - EMSGN(_("E966: Invalid line number: %ld"), (long)lnum); + EMSGN(_(e_invalid_lnum), (long)start_lnum); + return; + } + if (end_lnum < start_lnum || end_lnum > buf->b_ml.ml_line_count) + { + EMSGN(_(e_invalid_lnum), (long)end_lnum); return; } - // Fetch the line to get the ml_line_len field updated. - proplen = get_text_props(buf, lnum, &props, TRUE); - textlen = buf->b_ml.ml_line_len - proplen * sizeof(textprop_T); - - if (col >= (colnr_T)textlen - 1) + for (lnum = start_lnum; lnum <= end_lnum; ++lnum) { - EMSGN(_(e_invalid_col), (long)col); - return; + colnr_T col; // start column + long length; // in bytes + + // Fetch the line to get the ml_line_len field updated. + proplen = get_text_props(buf, lnum, &props, TRUE); + textlen = buf->b_ml.ml_line_len - proplen * sizeof(textprop_T); + + if (lnum == start_lnum) + col = start_col; + else + col = 1; + if (col - 1 > (colnr_T)textlen) + { + EMSGN(_(e_invalid_col), (long)start_col); + return; + } + + if (lnum == end_lnum) + length = end_col - col + 1; + else + length = textlen - col + 1; + if (length > textlen) + length = textlen; // can include the end-of-line + if (length < 1) + length = 1; + + // Allocate the new line with space for the new proprety. + newtext = alloc(buf->b_ml.ml_line_len + sizeof(textprop_T)); + if (newtext == NULL) + return; + // Copy the text, including terminating NUL. + mch_memmove(newtext, buf->b_ml.ml_line_ptr, textlen); + + // Find the index where to insert the new property. + // Since the text properties are not aligned properly when stored with the + // text, we need to copy them as bytes before using it as a struct. + for (i = 0; i < proplen; ++i) + { + mch_memmove(&tmp_prop, props + i * sizeof(textprop_T), + sizeof(textprop_T)); + if (tmp_prop.tp_col >= col) + break; + } + newprops = newtext + textlen; + if (i > 0) + mch_memmove(newprops, props, sizeof(textprop_T) * i); + + tmp_prop.tp_col = col; + tmp_prop.tp_len = length; + tmp_prop.tp_id = id; + tmp_prop.tp_type = type->pt_id; + tmp_prop.tp_flags = (lnum > start_lnum ? TP_FLAG_CONT_PREV : 0) + | (lnum < end_lnum ? TP_FLAG_CONT_NEXT : 0); + mch_memmove(newprops + i * sizeof(textprop_T), &tmp_prop, + sizeof(textprop_T)); + + if (i < proplen) + mch_memmove(newprops + (i + 1) * sizeof(textprop_T), + props + i * sizeof(textprop_T), + sizeof(textprop_T) * (proplen - i)); + + if (buf->b_ml.ml_flags & ML_LINE_DIRTY) + vim_free(buf->b_ml.ml_line_ptr); + buf->b_ml.ml_line_ptr = newtext; + buf->b_ml.ml_line_len += sizeof(textprop_T); + buf->b_ml.ml_flags |= ML_LINE_DIRTY; } - // Allocate the new line with space for the new proprety. - newtext = alloc(buf->b_ml.ml_line_len + sizeof(textprop_T)); - if (newtext == NULL) - return; - // Copy the text, including terminating NUL. - mch_memmove(newtext, buf->b_ml.ml_line_ptr, textlen); - - // Find the index where to insert the new property. - // Since the text properties are not aligned properly when stored with the - // text, we need to copy them as bytes before using it as a struct. - for (i = 0; i < proplen; ++i) - { - mch_memmove(&tmp_prop, props + i * sizeof(textprop_T), - sizeof(textprop_T)); - if (tmp_prop.tp_col >= col) - break; - } - newprops = newtext + textlen; - if (i > 0) - mch_memmove(newprops, props, sizeof(textprop_T) * i); - - tmp_prop.tp_col = col; - tmp_prop.tp_len = length; - tmp_prop.tp_id = id; - tmp_prop.tp_type = type->pt_id; - tmp_prop.tp_flags = 0; - mch_memmove(newprops + i * sizeof(textprop_T), &tmp_prop, - sizeof(textprop_T)); - - if (i < proplen) - mch_memmove(newprops + (i + 1) * sizeof(textprop_T), - props + i * sizeof(textprop_T), - sizeof(textprop_T) * (proplen - i)); - - if (buf->b_ml.ml_flags & ML_LINE_DIRTY) - vim_free(buf->b_ml.ml_line_ptr); - buf->b_ml.ml_line_ptr = newtext; - buf->b_ml.ml_line_len += sizeof(textprop_T); - buf->b_ml.ml_flags |= ML_LINE_DIRTY; - redraw_buf_later(buf, NOT_VALID); } diff --git a/src/version.c b/src/version.c index 6956225c32..2b1d397a30 100644 --- a/src/version.c +++ b/src/version.c @@ -799,6 +799,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 634, /**/ 633, /**/ From e38197d50f7068c4b68043792d283da98e526ec3 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 24 Dec 2018 23:35:13 +0100 Subject: [PATCH 18/22] patch 8.1.0635: Coverity complains about null pointer use Problem: Coverity complains about null pointer use. Solution: Avoid using a null pointer. --- src/evalfunc.c | 2 +- src/version.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/evalfunc.c b/src/evalfunc.c index 99e68f7e50..a9ef60e40f 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -13586,7 +13586,7 @@ f_test_override(typval_T *argvars, typval_T *rettv UNUSED) EMSG(_(e_invarg)); else { - name = tv_get_string_chk(&argvars[0]); + name = tv_get_string(&argvars[0]); val = (int)tv_get_number(&argvars[1]); if (STRCMP(name, (char_u *)"redraw") == 0) diff --git a/src/version.c b/src/version.c index 2b1d397a30..0712bcc33b 100644 --- a/src/version.c +++ b/src/version.c @@ -799,6 +799,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 635, /**/ 634, /**/ From b413d2e6a8cc7b1611a41bfa9462b986393ca5fe Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Tue, 25 Dec 2018 23:15:46 +0100 Subject: [PATCH 19/22] patch 8.1.0636: line2byte() gives wrong values with text properties Problem: line2byte() gives wrong values with text properties. (Bjorn Linse) Solution: Compute byte offsets differently when text properties were added. (closes #3718) --- src/memline.c | 52 +++++++++++++++++++++++++++-------- src/proto/textprop.pro | 1 - src/structs.h | 3 +- src/testdir/test_textprop.vim | 12 ++++++++ src/textprop.c | 22 ++++++--------- src/version.c | 2 ++ 6 files changed, 66 insertions(+), 26 deletions(-) diff --git a/src/memline.c b/src/memline.c index eaa3b65aba..9f082660a5 100644 --- a/src/memline.c +++ b/src/memline.c @@ -3179,14 +3179,14 @@ ml_replace_len(linenr_T lnum, char_u *line_arg, colnr_T len_arg, int copy) curbuf->b_ml.ml_flags &= ~ML_LINE_DIRTY; #ifdef FEAT_TEXT_PROP - if (has_any_text_properties(curbuf)) + if (curbuf->b_has_textprop) // Need to fetch the old line to copy over any text properties. ml_get_buf(curbuf, lnum, TRUE); #endif } #ifdef FEAT_TEXT_PROP - if (has_any_text_properties(curbuf)) + if (curbuf->b_has_textprop) { size_t oldtextlen = STRLEN(curbuf->b_ml.ml_line_ptr) + 1; @@ -5131,6 +5131,7 @@ ml_updatechunk( { int count; /* number of entries in block */ int idx; + int end_idx; int text_end; int linecnt; @@ -5154,23 +5155,39 @@ ml_updatechunk( (long)(buf->b_ml.ml_locked_low) + 1; idx = curline - buf->b_ml.ml_locked_low; curline = buf->b_ml.ml_locked_high + 1; - if (idx == 0)/* first line in block, text at the end */ - text_end = dp->db_txt_end; - else - text_end = ((dp->db_index[idx - 1]) & DB_INDEX_MASK); - /* Compute index of last line to use in this MEMLINE */ + + // compute index of last line to use in this MEMLINE rest = count - idx; if (linecnt + rest > MLCS_MINL) { - idx += MLCS_MINL - linecnt - 1; + end_idx = idx + MLCS_MINL - linecnt - 1; linecnt = MLCS_MINL; } else { - idx = count - 1; + end_idx = count - 1; linecnt += rest; } - size += text_end - ((dp->db_index[idx]) & DB_INDEX_MASK); +#ifdef FEAT_TEXT_PROP + if (buf->b_has_textprop) + { + int i; + + // We cannot use the text pointers to get the text length, + // the text prop info would also be counted. Go over the + // lines. + for (i = end_idx; i < idx; ++i) + size += STRLEN((char_u *)dp + (dp->db_index[i] & DB_INDEX_MASK)) + 1; + } + else +#endif + { + if (idx == 0)/* first line in block, text at the end */ + text_end = dp->db_txt_end; + else + text_end = ((dp->db_index[idx - 1]) & DB_INDEX_MASK); + size += text_end - ((dp->db_index[end_idx]) & DB_INDEX_MASK); + } } buf->b_ml.ml_chunksize[curix].mlcs_numlines = linecnt; buf->b_ml.ml_chunksize[curix + 1].mlcs_numlines -= linecnt; @@ -5360,7 +5377,20 @@ ml_find_line_or_offset(buf_T *buf, linenr_T lnum, long *offp) idx++; } } - len = text_end - ((dp->db_index[idx]) & DB_INDEX_MASK); +#ifdef FEAT_TEXT_PROP + if (buf->b_has_textprop) + { + int i; + + // cannot use the db_index pointer, need to get the actual text + // lengths. + len = 0; + for (i = start_idx; i <= idx; ++i) + len += STRLEN((char_u *)dp + ((dp->db_index[idx]) & DB_INDEX_MASK)) + 1; + } + else +#endif + len = text_end - ((dp->db_index[idx]) & DB_INDEX_MASK); size += len; if (offset != 0 && size >= offset) { diff --git a/src/proto/textprop.pro b/src/proto/textprop.pro index 62aef4f6b9..52bb6bcc37 100644 --- a/src/proto/textprop.pro +++ b/src/proto/textprop.pro @@ -1,6 +1,5 @@ /* textprop.c */ void f_prop_add(typval_T *argvars, typval_T *rettv); -int has_any_text_properties(buf_T *buf); int get_text_props(buf_T *buf, linenr_T lnum, char_u **props, int will_change); proptype_T *text_prop_type_by_id(buf_T *buf, int id); void f_prop_clear(typval_T *argvars, typval_T *rettv); diff --git a/src/structs.h b/src/structs.h index e4311eda41..2f2795a128 100644 --- a/src/structs.h +++ b/src/structs.h @@ -2411,7 +2411,8 @@ struct file_buffer dict_T *b_vars; /* internal variables, local to buffer */ #endif #ifdef FEAT_TEXT_PROP - hashtab_T *b_proptypes; /* text property types local to buffer */ + int b_has_textprop; // TRUE when text props were added + hashtab_T *b_proptypes; // text property types local to buffer #endif #if defined(FEAT_BEVAL) && defined(FEAT_EVAL) diff --git a/src/testdir/test_textprop.vim b/src/testdir/test_textprop.vim index 081d4ab5f1..3ec6ea81db 100644 --- a/src/testdir/test_textprop.vim +++ b/src/testdir/test_textprop.vim @@ -226,5 +226,17 @@ func Test_prop_multiline() call prop_type_delete('comment') endfunc +func Test_prop_byteoff() + call prop_type_add('comment', {'highlight': 'Directory'}) + new + call setline(1, ['line1', 'line2', '']) + call assert_equal(13, line2byte(3)) + call prop_add(1, 1, {'end_col': 3, 'type': 'comment'}) + call assert_equal(13, line2byte(3)) + + bwipe! + call prop_type_delete('comment') +endfunc + " TODO: screenshot test with highlighting diff --git a/src/textprop.c b/src/textprop.c index f5b977a7a0..ade99a7bf6 100644 --- a/src/textprop.c +++ b/src/textprop.c @@ -17,12 +17,16 @@ * Text properties have a type, which can be used to specify highlighting. * * TODO: + * - mismatch in column 1 being the first column + * - Let props overrule syntax HL. * - When deleting a line where a prop ended, adjust flag of previous line. * - When deleting a line where a prop started, adjust flag of next line. * - When inserting a line add props that continue from previous line. * - Adjust property column and length when text is inserted/deleted * - Add an arrray for global_proptypes, to quickly lookup a proptype by ID * - Add an arrray for b_proptypes, to quickly lookup a proptype by ID + * - Also test line2byte() with many lines, so that ml_updatechunk() is taken + * into account. * - add mechanism to keep track of changed lines. */ @@ -261,7 +265,7 @@ f_prop_add(typval_T *argvars, typval_T *rettv UNUSED) length = end_col - col + 1; else length = textlen - col + 1; - if (length > textlen) + if (length > (long)textlen) length = textlen; // can include the end-of-line if (length < 1) length = 1; @@ -308,19 +312,10 @@ f_prop_add(typval_T *argvars, typval_T *rettv UNUSED) buf->b_ml.ml_flags |= ML_LINE_DIRTY; } + buf->b_has_textprop = TRUE; // this is never reset redraw_buf_later(buf, NOT_VALID); } -/* - * Return TRUE if any text properties are defined globally or for buffer - * "buf". - */ - int -has_any_text_properties(buf_T *buf) -{ - return buf->b_proptypes != NULL || global_proptypes != NULL; -} - /* * Fetch the text properties for line "lnum" in buffer "buf". * Returns the number of text properties and, when non-zero, a pointer to the @@ -334,8 +329,9 @@ get_text_props(buf_T *buf, linenr_T lnum, char_u **props, int will_change) size_t textlen; size_t proplen; - // Be quick when no text property types are defined. - if (!has_any_text_properties(buf)) + // Be quick when no text property types have been defined or the buffer, + // unless we are adding one. + if (!buf->b_has_textprop && !will_change) return 0; // Fetch the line to get the ml_line_len field updated. diff --git a/src/version.c b/src/version.c index 0712bcc33b..b2dbe38be5 100644 --- a/src/version.c +++ b/src/version.c @@ -799,6 +799,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 636, /**/ 635, /**/ From 4604fbbbff9e1f924e76a6b4695626b519bd4030 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Tue, 25 Dec 2018 23:37:02 +0100 Subject: [PATCH 20/22] patch 8.1.0637: nsis file no longer used Problem: Nsis file no longer used. Solution: Remove the file. (Ken Takata) --- Filelist | 1 - nsis/vimrc.ini | 68 -------------------------------------------------- src/version.c | 2 ++ 3 files changed, 2 insertions(+), 69 deletions(-) delete mode 100644 nsis/vimrc.ini diff --git a/Filelist b/Filelist index 6daf111f5d..de0c9b073e 100644 --- a/Filelist +++ b/Filelist @@ -475,7 +475,6 @@ SRC_DOS = \ src/xxd/Make_mvc.mak \ nsis/gvim.nsi \ nsis/gvim_version.nsh \ - nsis/vimrc.ini \ nsis/README.txt \ nsis/lang/*.nsi \ uninstal.txt \ diff --git a/nsis/vimrc.ini b/nsis/vimrc.ini deleted file mode 100644 index a3e9ecb5bb..0000000000 --- a/nsis/vimrc.ini +++ /dev/null @@ -1,68 +0,0 @@ -[Settings] -NumFields=7 - -[Field 1] -Type=GroupBox -Left=0 -Right=-1 -Top=0 -Bottom=53 -Text=" Key remapping " - -[Field 2] -Type=radiobutton -Text=Do not remap keys for Windows behavior (Default) -Left=10 -Right=-10 -Top=17 -Bottom=25 -State=1 -Flags=GROUP - -[Field 3] -Type=radiobutton -Text=Remap a few keys for Windows behavior (, , , , , etc) -Left=10 -Right=-10 -Top=30 -Bottom=47 -State=0 -Flags=NOTABSTOP - -[Field 4] -Type=GroupBox -Left=0 -Right=-1 -Top=55 -Bottom=-5 -Text=" Mouse behavior " - -[Field 5] -Type=radiobutton -Text=Right button extends selection, left button starts visual mode (Unix) -Left=10 -Right=-5 -Top=72 -Bottom=80 -State=0 -Flags=GROUP - -[Field 6] -Type=radiobutton -Text=Right button has a popup menu, left button starts select mode (Windows) -Left=10 -Right=-5 -Top=85 -Bottom=93 -State=0 -Flags=NOTABSTOP - -[Field 7] -Type=radiobutton -Text=Right button has a popup menu, left button starts visual mode (Default) -Left=10 -Right=-5 -Top=98 -Bottom=106 -State=1 -Flags=NOTABSTOP diff --git a/src/version.c b/src/version.c index b2dbe38be5..c51a46d585 100644 --- a/src/version.c +++ b/src/version.c @@ -799,6 +799,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 637, /**/ 636, /**/ From 48f88ac85be8446a42a03cec45264eac21f9eba8 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 26 Dec 2018 00:25:20 +0100 Subject: [PATCH 21/22] patch 8.1.0638: text property highlighting is off by one column Problem: Text property highlighting is off by one column. (Bjorn Linse) Solution: Update text property highlighting earlier. Let it overrule syntax highlighting. --- src/screen.c | 138 ++++++++++++++++++++++++++------------------------ src/structs.h | 2 +- src/version.c | 2 + 3 files changed, 75 insertions(+), 67 deletions(-) diff --git a/src/screen.c b/src/screen.c index ed1d4a3914..62f0d136d7 100644 --- a/src/screen.c +++ b/src/screen.c @@ -4294,6 +4294,66 @@ win_line( } #endif +#ifdef FEAT_TEXT_PROP + if (text_props != NULL) + { + int pi; + + // Check if any active property ends. + for (pi = 0; pi < text_props_active; ++pi) + { + int tpi = text_prop_idxs[pi]; + + if (col >= text_props[tpi].tp_col - 1 + + text_props[tpi].tp_len) + { + if (pi + 1 < text_props_active) + mch_memmove(text_prop_idxs + pi, + text_prop_idxs + pi + 1, + sizeof(int) + * (text_props_active - (pi + 1))); + --text_props_active; + --pi; + } + } + + // Add any text property that starts in this column. + while (text_prop_next < text_prop_count + && col >= text_props[text_prop_next].tp_col - 1) + text_prop_idxs[text_props_active++] = text_prop_next++; + + text_prop_type = NULL; + if (text_props_active > 0) + { + int max_priority = INT_MIN; + int max_col = 0; + + // Get the property type with the highest priority + // and/or starting last. + for (pi = 0; pi < text_props_active; ++pi) + { + int tpi = text_prop_idxs[pi]; + proptype_T *pt; + + pt = text_prop_type_by_id( + curwin->w_buffer, text_props[tpi].tp_type); + if (pt != NULL + && (pt->pt_priority > max_priority + || (pt->pt_priority == max_priority + && text_props[tpi].tp_col >= max_col))) + { + text_prop_type = pt; + max_priority = pt->pt_priority; + max_col = text_props[tpi].tp_col; + } + } + if (text_prop_type != NULL) + text_prop_attr = + syn_id2attr(text_prop_type->pt_hl_id); + } + } +#endif + /* Decide which of the highlight attributes to use. */ attr_pri = TRUE; #ifdef LINE_ATTR @@ -4653,8 +4713,8 @@ win_line( #endif #ifdef FEAT_SYN_HL - /* Get syntax attribute, unless still at the start of the line - * (double-wide char that doesn't fit). */ + // Get syntax attribute, unless still at the start of the line + // (double-wide char that doesn't fit). v = (long)(ptr - line); if (has_syntax && v > 0) { @@ -4686,10 +4746,16 @@ win_line( line = ml_get_buf(wp->w_buffer, lnum, FALSE); ptr = line + v; - if (!attr_pri) - char_attr = syntax_attr; - else - char_attr = hl_combine_attr(syntax_attr, char_attr); +# ifdef FEAT_TEXT_PROP + // Text properties overrule syntax highlighting. + if (text_prop_attr == 0) +#endif + { + if (!attr_pri) + char_attr = syntax_attr; + else + char_attr = hl_combine_attr(syntax_attr, char_attr); + } # ifdef FEAT_CONCEAL /* no concealing past the end of the line, it interferes * with line highlighting */ @@ -4701,66 +4767,6 @@ win_line( } #endif -#ifdef FEAT_TEXT_PROP - if (text_props != NULL) - { - int pi; - - // Check if any active property ends. - for (pi = 0; pi < text_props_active; ++pi) - { - int tpi = text_prop_idxs[pi]; - - if (col >= text_props[tpi].tp_col - 1 - + text_props[tpi].tp_len) - { - if (pi + 1 < text_props_active) - mch_memmove(text_prop_idxs + pi, - text_prop_idxs + pi + 1, - sizeof(int) - * (text_props_active - (pi + 1))); - --text_props_active; - --pi; - } - } - - // Add any text property that starts in this column. - while (text_prop_next < text_prop_count - && col >= text_props[text_prop_next].tp_col - 1) - text_prop_idxs[text_props_active++] = text_prop_next++; - - text_prop_type = NULL; - if (text_props_active > 0) - { - int max_priority = INT_MIN; - int max_col = 0; - - // Get the property type with the highest priority - // and/or starting last. - for (pi = 0; pi < text_props_active; ++pi) - { - int tpi = text_prop_idxs[pi]; - proptype_T *pt; - - pt = text_prop_type_by_id( - curwin->w_buffer, text_props[tpi].tp_type); - if (pt != NULL - && (pt->pt_priority > max_priority - || (pt->pt_priority == max_priority - && text_props[tpi].tp_col >= max_col))) - { - text_prop_type = pt; - max_priority = pt->pt_priority; - max_col = text_props[tpi].tp_col; - } - } - if (text_prop_type != NULL) - text_prop_attr = - syn_id2attr(text_prop_type->pt_hl_id); - } - } -#endif - #ifdef FEAT_SPELL /* Check spelling (unless at the end of the line). * Only do this when there is no syntax highlighting, the diff --git a/src/structs.h b/src/structs.h index 2f2795a128..aa59bff245 100644 --- a/src/structs.h +++ b/src/structs.h @@ -705,7 +705,7 @@ typedef struct memline */ typedef struct textprop_S { - colnr_T tp_col; // start column + colnr_T tp_col; // start column (one based) colnr_T tp_len; // length in bytes int tp_id; // identifier int tp_type; // property type diff --git a/src/version.c b/src/version.c index c51a46d585..5cffe09c87 100644 --- a/src/version.c +++ b/src/version.c @@ -799,6 +799,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 638, /**/ 637, /**/ From 8cf734e024af56707a1165bcdfee42364695ec8e Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 26 Dec 2018 01:09:00 +0100 Subject: [PATCH 22/22] patch 8.1.0639: text properties test fails on MS-Windows Problem: text properties test fails on MS-Windows Solution: Set fileformat to "unix". --- src/testdir/test_textprop.vim | 1 + src/version.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/testdir/test_textprop.vim b/src/testdir/test_textprop.vim index 3ec6ea81db..ccf7d28460 100644 --- a/src/testdir/test_textprop.vim +++ b/src/testdir/test_textprop.vim @@ -230,6 +230,7 @@ func Test_prop_byteoff() call prop_type_add('comment', {'highlight': 'Directory'}) new call setline(1, ['line1', 'line2', '']) + set ff=unix call assert_equal(13, line2byte(3)) call prop_add(1, 1, {'end_col': 3, 'type': 'comment'}) call assert_equal(13, line2byte(3)) diff --git a/src/version.c b/src/version.c index 5cffe09c87..30f2899624 100644 --- a/src/version.c +++ b/src/version.c @@ -799,6 +799,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 639, /**/ 638, /**/