diff --git a/runtime/doc/channel.txt b/runtime/doc/channel.txt index bfb62f57b2..c28b90ed2c 100644 --- a/runtime/doc/channel.txt +++ b/runtime/doc/channel.txt @@ -1,4 +1,4 @@ -*channel.txt* For Vim version 7.4. Last change: 2016 May 24 +*channel.txt* For Vim version 7.4. Last change: 2016 May 29 VIM REFERENCE MANUAL by Bram Moolenaar @@ -578,8 +578,8 @@ See |job_setoptions()| and |ch_setoptions()|. "exit_cb": handler Callback for when the job ends. The arguments are the job and the exit status. Vim checks about every 10 seconds for jobs that ended. - The callback can also be triggered by calling - |job_status()|. + The check also be triggered by calling |job_status()|, + which may then invoke the exit_cb handler. Note that data can be buffered, callbacks may still be called after the process ends. *job-timeout* @@ -625,18 +625,22 @@ See |job_setoptions()| and |ch_setoptions()|. "out_io": "null" disconnect stdout (goes to /dev/null) "out_io": "pipe" stdout is connected to the channel (default) "out_io": "file" stdout writes to a file -"out_io": "buffer" stdout appends to a buffer +"out_io": "buffer" stdout appends to a buffer (see below) "out_name": "/path/file" the name of the file or buffer to write to "out_buf": number the number of the buffer to write to +"out_modifiable": 0 when writing to a buffer, 'modifiable' will be off + (see below) *job-err_io* *err_name* *err_buf* "err_io": "out" stderr messages to go to stdout "err_io": "null" disconnect stderr (goes to /dev/null) "err_io": "pipe" stderr is connected to the channel (default) "err_io": "file" stderr writes to a file -"err_io": "buffer" stderr appends to a buffer +"err_io": "buffer" stderr appends to a buffer (see below) "err_name": "/path/file" the name of the file or buffer to write to "err_buf": number the number of the buffer to write to +"err_modifiable": 0 when writing to a buffer, 'modifiable' will be off + (see below) "block_write": number only for testing: pretend every other write to stdin will block @@ -663,6 +667,15 @@ used to get the buffer number. For a new buffer 'buftype' is set to "nofile" and 'bufhidden' to "hide". If you prefer other settings, create the buffer first and pass the buffer number. +The "out_modifiable" and "err_modifiable" options can be used to set the +'modifiable' option off, or write to a buffer that has 'modifiable' off. That +means that lines will be appended to the buffer, but the user can't easily +change the buffer. + +When an existing buffer is to be written where 'modifiable' is off and the +"out_modifiable" or "err_modifiable" options is not zero, an error is given +and the buffer will not be written to. + When the buffer written to is displayed in a window and the cursor is in the first column of the last line, the cursor will be moved to the newly added line and the window is scrolled up to show the cursor if needed. diff --git a/src/Makefile b/src/Makefile index 237ddf0dfa..f59803928e 100644 --- a/src/Makefile +++ b/src/Makefile @@ -582,7 +582,7 @@ CClink = $(CC) #CFLAGS = -g -O2 '-DSTARTUPTIME="vimstartup"' -fno-strength-reduce -Wall -Wmissing-prototypes # Use this with GCC to check for mistakes, unused arguments, etc. -#CFLAGS = -g -Wall -Wextra -Wmissing-prototypes -Wunreachable-code -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1 +#CFLAGS = -g -Wall -Wextra -Wshadow -Wmissing-prototypes -Wunreachable-code -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1 #CFLAGS = -g -O2 -Wall -Wextra -Wmissing-prototypes -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1 -DU_DEBUG #PYTHON_CFLAGS_EXTRA = -Wno-missing-field-initializers #MZSCHEME_CFLAGS_EXTRA = -Wno-unreachable-code -Wno-unused-parameter @@ -1960,6 +1960,10 @@ test check: cd testdir; $(MAKE) -f Makefile $(GUI_TESTTARGET) VIMPROG=../$(VIMTARGET) $(GUI_TESTARG) SCRIPTSOURCE=../$(SCRIPTSOURCE) $(MAKE) -f Makefile unittest +# Run the tests with the GUI. Assumes vim/gvim was already built +testgui: + cd testdir; $(MAKE) -f Makefile $(GUI_TESTTARGET) VIMPROG=../$(VIMTARGET) GUI_FLAG=-g $(GUI_TESTARG) SCRIPTSOURCE=../$(SCRIPTSOURCE) + benchmark: cd testdir; $(MAKE) -f Makefile benchmark VIMPROG=../$(VIMTARGET) SCRIPTSOURCE=../$(SCRIPTSOURCE) @@ -2074,6 +2078,7 @@ test_arglist \ test_viminfo \ test_viml \ test_visual \ + test_window_cmd \ test_window_id \ test_alot_latin \ test_alot_utf8 \ diff --git a/src/buffer.c b/src/buffer.c index e5f9339b10..86dad1148c 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -1653,6 +1653,7 @@ do_autochdir(void) * If (flags & BLN_CURBUF) is TRUE, may use current buffer. * If (flags & BLN_LISTED) is TRUE, add new buffer to buffer list. * If (flags & BLN_DUMMY) is TRUE, don't count it as a real buffer. + * If (flags & BLN_NEW) is TRUE, don't use an existing buffer. * This is the ONLY way to create a new buffer. */ static int top_file_num = 1; /* highest file number */ @@ -1680,7 +1681,7 @@ buflist_new( if (sfname == NULL || mch_stat((char *)sfname, &st) < 0) st.st_dev = (dev_T)-1; #endif - if (ffname != NULL && !(flags & BLN_DUMMY) && (buf = + if (ffname != NULL && !(flags & (BLN_DUMMY | BLN_NEW)) && (buf = #ifdef UNIX buflist_findname_stat(ffname, &st) #else diff --git a/src/channel.c b/src/channel.c index 992c6bda35..290cedb051 100644 --- a/src/channel.c +++ b/src/channel.c @@ -1100,11 +1100,15 @@ find_buffer(char_u *name, int err) buf_T *save_curbuf = curbuf; if (name != NULL && *name != NUL) + { buf = buflist_findname(name); + if (buf == NULL) + buf = buflist_findname_exp(name); + } if (buf == NULL) { buf = buflist_new(name == NULL || *name == NUL ? NULL : name, - NULL, (linenr_T)0, BLN_LISTED); + NULL, (linenr_T)0, BLN_LISTED | BLN_NEW); if (buf == NULL) return NULL; buf_copy_options(buf, BCO_ENTER); @@ -1230,9 +1234,20 @@ channel_set_options(channel_T *channel, jobopt_T *opt) } if (buf != NULL) { - ch_logs(channel, "writing out to buffer '%s'", + if (opt->jo_set & JO_OUT_MODIFIABLE) + channel->ch_part[PART_OUT].ch_nomodifiable = + !opt->jo_modifiable[PART_OUT]; + + if (!buf->b_p_ma && !channel->ch_part[PART_OUT].ch_nomodifiable) + { + EMSG(_(e_modifiable)); + } + else + { + ch_logs(channel, "writing out to buffer '%s'", (char *)buf->b_ffname); - channel->ch_part[PART_OUT].ch_buffer = buf; + channel->ch_part[PART_OUT].ch_buffer = buf; + } } } @@ -1257,9 +1272,19 @@ channel_set_options(channel_T *channel, jobopt_T *opt) buf = find_buffer(opt->jo_io_name[PART_ERR], TRUE); if (buf != NULL) { - ch_logs(channel, "writing err to buffer '%s'", + if (opt->jo_set & JO_ERR_MODIFIABLE) + channel->ch_part[PART_ERR].ch_nomodifiable = + !opt->jo_modifiable[PART_ERR]; + if (!buf->b_p_ma && !channel->ch_part[PART_ERR].ch_nomodifiable) + { + EMSG(_(e_modifiable)); + } + else + { + ch_logs(channel, "writing err to buffer '%s'", (char *)buf->b_ffname); - channel->ch_part[PART_ERR].ch_buffer = buf; + channel->ch_part[PART_ERR].ch_buffer = buf; + } } } @@ -1306,6 +1331,7 @@ write_buf_line(buf_T *buf, linenr_T lnum, channel_T *channel) int len = (int)STRLEN(line); char_u *p; + /* Need to make a copy to be able to append a NL. */ if ((p = alloc(len + 2)) == NULL) return; STRCPY(p, line); @@ -2127,11 +2153,23 @@ invoke_one_time_callback( } static void -append_to_buffer(buf_T *buffer, char_u *msg, channel_T *channel) +append_to_buffer(buf_T *buffer, char_u *msg, channel_T *channel, int part) { buf_T *save_curbuf = curbuf; linenr_T lnum = buffer->b_ml.ml_line_count; int save_write_to = buffer->b_write_to_channel; + chanpart_T *ch_part = &channel->ch_part[part]; + int save_p_ma = buffer->b_p_ma; + + if (!buffer->b_p_ma && !ch_part->ch_nomodifiable) + { + if (!ch_part->ch_nomod_error) + { + ch_error(channel, "Buffer is not modifiable, cannot append"); + ch_part->ch_nomod_error = TRUE; + } + return; + } /* If the buffer is also used as input insert above the last * line. Don't write these lines. */ @@ -2144,6 +2182,7 @@ append_to_buffer(buf_T *buffer, char_u *msg, channel_T *channel) /* Append to the buffer */ ch_logn(channel, "appending line %d to buffer", (int)lnum + 1); + buffer->b_p_ma = TRUE; curbuf = buffer; u_sync(TRUE); /* ignore undo failure, undo is not very useful here */ @@ -2152,6 +2191,10 @@ append_to_buffer(buf_T *buffer, char_u *msg, channel_T *channel) ml_append(lnum, msg, 0, FALSE); appended_lines_mark(lnum, 1L); curbuf = save_curbuf; + if (ch_part->ch_nomodifiable) + buffer->b_p_ma = FALSE; + else + buffer->b_p_ma = save_p_ma; if (buffer->b_nwindows > 0) { @@ -2379,7 +2422,7 @@ may_invoke_callback(channel_T *channel, int part) /* JSON or JS mode: re-encode the message. */ msg = json_encode(listtv, ch_mode); if (msg != NULL) - append_to_buffer(buffer, msg, channel); + append_to_buffer(buffer, msg, channel, part); } if (callback != NULL) @@ -2587,7 +2630,6 @@ channel_close(channel_T *channel, int invoke_close_cb) clear_tv(&rettv); channel_need_redraw = TRUE; } - --channel->ch_refcount; /* the callback is only called once */ vim_free(channel->ch_close_cb); @@ -2595,6 +2637,8 @@ channel_close(channel_T *channel, int invoke_close_cb) partial_unref(channel->ch_close_partial); channel->ch_close_partial = NULL; + --channel->ch_refcount; + if (channel_need_redraw) { channel_need_redraw = FALSE; @@ -2889,6 +2933,11 @@ channel_close_on_error(channel_T *channel, char *func) * died. Don't close the channel right away, it may be the wrong moment * to invoke callbacks. */ channel->ch_to_be_closed = TRUE; + +#ifdef FEAT_GUI + /* Stop listening to GUI events right away. */ + channel_gui_unregister(channel); +#endif } static void @@ -2903,7 +2952,7 @@ channel_close_now(channel_T *channel) /* * Read from channel "channel" for as long as there is something to read. * "part" is PART_SOCK, PART_OUT or PART_ERR. - * The data is put in the read queue. + * The data is put in the read queue. No callbacks are invoked here. */ #ifndef FEAT_GUI_MACVIM static void @@ -3815,6 +3864,8 @@ free_job_options(jobopt_T *opt) partial_unref(opt->jo_err_partial); if (opt->jo_close_partial != NULL) partial_unref(opt->jo_close_partial); + if (opt->jo_exit_partial != NULL) + partial_unref(opt->jo_exit_partial); } /* @@ -3933,6 +3984,16 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported) return FAIL; } } + else if (STRCMP(hi->hi_key, "out_modifiable") == 0 + || STRCMP(hi->hi_key, "err_modifiable") == 0) + { + part = part_from_char(*hi->hi_key); + + if (!(supported & JO_OUT_IO)) + break; + opt->jo_set |= JO_OUT_MODIFIABLE << (part - PART_OUT); + opt->jo_modifiable[part] = get_tv_number(item); + } else if (STRCMP(hi->hi_key, "in_top") == 0 || STRCMP(hi->hi_key, "in_bot") == 0) { @@ -4017,6 +4078,18 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported) return FAIL; } } + else if (STRCMP(hi->hi_key, "exit_cb") == 0) + { + if (!(supported & JO_EXIT_CB)) + break; + opt->jo_set |= JO_EXIT_CB; + opt->jo_exit_cb = get_callback(item, &opt->jo_exit_partial); + if (opt->jo_exit_cb == NULL) + { + EMSG2(_(e_invarg2), "exit_cb"); + return FAIL; + } + } else if (STRCMP(hi->hi_key, "waittime") == 0) { if (!(supported & JO_WAITTIME)) @@ -4079,25 +4152,6 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported) return FAIL; } } - else if (STRCMP(hi->hi_key, "exit_cb") == 0) - { - if (!(supported & JO_EXIT_CB)) - break; - opt->jo_set |= JO_EXIT_CB; - if (item->v_type == VAR_PARTIAL && item->vval.v_partial != NULL) - { - opt->jo_exit_partial = item->vval.v_partial; - opt->jo_exit_cb = item->vval.v_partial->pt_name; - } - else - opt->jo_exit_cb = get_tv_string_buf_chk( - item, opt->jo_ecb_buf); - if (opt->jo_exit_cb == NULL) - { - EMSG2(_(e_invarg2), "exit_cb"); - return FAIL; - } - } else if (STRCMP(hi->hi_key, "block_write") == 0) { if (!(supported & JO_BLOCK_WRITE)) @@ -4203,6 +4257,15 @@ job_free(job_T *job) } } +#if defined(EXITFREE) || defined(PROTO) + void +job_free_all(void) +{ + while (first_job != NULL) + job_free(first_job); +} +#endif + /* * Return TRUE if the job should not be freed yet. Do not free the job when * it has not ended yet and there is a "stoponexit" flag, an exit callback diff --git a/src/eval.c b/src/eval.c index 18c1cb0786..38805a57a4 100644 --- a/src/eval.c +++ b/src/eval.c @@ -445,16 +445,17 @@ static long list_idx_of_item(list_T *l, listitem_T *item); static int list_extend(list_T *l1, list_T *l2, listitem_T *bef); static int list_concat(list_T *l1, list_T *l2, typval_T *tv); static list_T *list_copy(list_T *orig, int deep, int copyID); -static char_u *list2string(typval_T *tv, int copyID); -static int list_join_inner(garray_T *gap, list_T *l, char_u *sep, int echo_style, int copyID, garray_T *join_gap); -static int list_join(garray_T *gap, list_T *l, char_u *sep, int echo, int copyID); +static char_u *list2string(typval_T *tv, int copyID, int restore_copyID); +static int list_join_inner(garray_T *gap, list_T *l, char_u *sep, int echo_style, int restore_copyID, int copyID, garray_T *join_gap); +static int list_join(garray_T *gap, list_T *l, char_u *sep, int echo_style, int restore_copyID, int copyID); static int free_unref_items(int copyID); static dictitem_T *dictitem_copy(dictitem_T *org); static void dictitem_remove(dict_T *dict, dictitem_T *item); static dict_T *dict_copy(dict_T *orig, int deep, int copyID); static long dict_len(dict_T *d); -static char_u *dict2string(typval_T *tv, int copyID); +static char_u *dict2string(typval_T *tv, int copyID, int restore_copyID); static int get_dict_tv(char_u **arg, typval_T *rettv, int evaluate); +static char_u *echo_string_core(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID, int echo_style, int restore_copyID, int dict_val); static char_u *echo_string(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID); static char_u *string_quote(char_u *str, int function); static int get_env_tv(char_u **arg, typval_T *rettv, int evaluate); @@ -1462,7 +1463,7 @@ eval_to_string( ga_init2(&ga, (int)sizeof(char), 80); if (tv.vval.v_list != NULL) { - list_join(&ga, tv.vval.v_list, (char_u *)"\n", TRUE, 0); + list_join(&ga, tv.vval.v_list, (char_u *)"\n", TRUE, FALSE, 0); if (tv.vval.v_list->lv_len > 0) ga_append(&ga, NL); } @@ -6766,7 +6767,7 @@ vimlist_remove(list_T *l, listitem_T *item, listitem_T *item2) * May return NULL. */ static char_u * -list2string(typval_T *tv, int copyID) +list2string(typval_T *tv, int copyID, int restore_copyID) { garray_T ga; @@ -6774,7 +6775,8 @@ list2string(typval_T *tv, int copyID) return NULL; ga_init2(&ga, (int)sizeof(char), 80); ga_append(&ga, '['); - if (list_join(&ga, tv->vval.v_list, (char_u *)", ", FALSE, copyID) == FAIL) + if (list_join(&ga, tv->vval.v_list, (char_u *)", ", + FALSE, restore_copyID, copyID) == FAIL) { vim_free(ga.ga_data); return NULL; @@ -6795,6 +6797,7 @@ list_join_inner( list_T *l, char_u *sep, int echo_style, + int restore_copyID, int copyID, garray_T *join_gap) /* to keep each list item string */ { @@ -6811,10 +6814,8 @@ list_join_inner( /* Stringify each item in the list. */ for (item = l->lv_first; item != NULL && !got_int; item = item->li_next) { - if (echo_style) - s = echo_string(&item->li_tv, &tofree, numbuf, copyID); - else - s = tv2string(&item->li_tv, &tofree, numbuf, copyID); + s = echo_string_core(&item->li_tv, &tofree, numbuf, copyID, + echo_style, restore_copyID, FALSE); if (s == NULL) return FAIL; @@ -6873,6 +6874,7 @@ list_join( list_T *l, char_u *sep, int echo_style, + int restore_copyID, int copyID) { garray_T join_ga; @@ -6883,7 +6885,8 @@ list_join( if (l->lv_len < 1) return OK; /* nothing to do */ ga_init2(&join_ga, (int)sizeof(join_T), l->lv_len); - retval = list_join_inner(gap, l, sep, echo_style, copyID, &join_ga); + retval = list_join_inner(gap, l, sep, echo_style, restore_copyID, + copyID, &join_ga); /* Dispose each item in join_ga. */ if (join_ga.ga_data != NULL) @@ -7046,6 +7049,10 @@ garbage_collect(int testing) abort = abort || set_ref_in_nb_channel(copyID); #endif +#ifdef FEAT_TIMERS + abort = abort || set_ref_in_timer(copyID); +#endif + if (!abort) { /* @@ -7829,7 +7836,7 @@ get_dict_number(dict_T *d, char_u *key) * May return NULL. */ static char_u * -dict2string(typval_T *tv, int copyID) +dict2string(typval_T *tv, int copyID, int restore_copyID) { garray_T ga; int first = TRUE; @@ -7864,7 +7871,8 @@ dict2string(typval_T *tv, int copyID) vim_free(tofree); } ga_concat(&ga, (char_u *)": "); - s = tv2string(&HI2DI(hi)->di_tv, &tofree, numbuf, copyID); + s = echo_string_core(&HI2DI(hi)->di_tv, &tofree, numbuf, copyID, + FALSE, restore_copyID, TRUE); if (s != NULL) ga_concat(&ga, s); vim_free(tofree); @@ -8022,16 +8030,23 @@ get_var_special_name(int nr) * Return a string with the string representation of a variable. * If the memory is allocated "tofree" is set to it, otherwise NULL. * "numbuf" is used for a number. - * Does not put quotes around strings, as ":echo" displays values. * When "copyID" is not NULL replace recursive lists and dicts with "...". + * When both "echo_style" and "dict_val" are FALSE, put quotes around stings as + * "string()", otherwise does not put quotes around strings, as ":echo" + * displays values. + * When "restore_copyID" is FALSE, repeated items in dictionaries and lists + * are replaced with "...". * May return NULL. */ static char_u * -echo_string( +echo_string_core( typval_T *tv, char_u **tofree, char_u *numbuf, - int copyID) + int copyID, + int echo_style, + int restore_copyID, + int dict_val) { static int recurse = 0; char_u *r = NULL; @@ -8053,9 +8068,30 @@ echo_string( switch (tv->v_type) { + case VAR_STRING: + if (echo_style && !dict_val) + { + *tofree = NULL; + r = get_tv_string_buf(tv, numbuf); + } + else + { + *tofree = string_quote(tv->vval.v_string, FALSE); + r = *tofree; + } + break; + case VAR_FUNC: - *tofree = NULL; - r = tv->vval.v_string; + if (echo_style) + { + *tofree = NULL; + r = tv->vval.v_string; + } + else + { + *tofree = string_quote(tv->vval.v_string, TRUE); + r = *tofree; + } break; case VAR_PARTIAL: @@ -8110,15 +8146,20 @@ echo_string( *tofree = NULL; r = NULL; } - else if (copyID != 0 && tv->vval.v_list->lv_copyID == copyID) + else if (copyID != 0 && tv->vval.v_list->lv_copyID == copyID + && tv->vval.v_list->lv_len > 0) { *tofree = NULL; r = (char_u *)"[...]"; } else { + int old_copyID = tv->vval.v_list->lv_copyID; + tv->vval.v_list->lv_copyID = copyID; - *tofree = list2string(tv, copyID); + *tofree = list2string(tv, copyID, restore_copyID); + if (restore_copyID) + tv->vval.v_list->lv_copyID = old_copyID; r = *tofree; } break; @@ -8129,20 +8170,23 @@ echo_string( *tofree = NULL; r = NULL; } - else if (copyID != 0 && tv->vval.v_dict->dv_copyID == copyID) + else if (copyID != 0 && tv->vval.v_dict->dv_copyID == copyID + && tv->vval.v_dict->dv_hashtab.ht_used != 0) { *tofree = NULL; r = (char_u *)"{...}"; } else { + int old_copyID = tv->vval.v_dict->dv_copyID; tv->vval.v_dict->dv_copyID = copyID; - *tofree = dict2string(tv, copyID); + *tofree = dict2string(tv, copyID, restore_copyID); + if (restore_copyID) + tv->vval.v_dict->dv_copyID = old_copyID; r = *tofree; } break; - case VAR_STRING: case VAR_NUMBER: case VAR_UNKNOWN: case VAR_JOB: @@ -8170,6 +8214,24 @@ echo_string( return r; } +/* + * Return a string with the string representation of a variable. + * If the memory is allocated "tofree" is set to it, otherwise NULL. + * "numbuf" is used for a number. + * Does not put quotes around strings, as ":echo" displays values. + * When "copyID" is not NULL replace recursive lists and dicts with "...". + * May return NULL. + */ + static char_u * +echo_string( + typval_T *tv, + char_u **tofree, + char_u *numbuf, + int copyID) +{ + return echo_string_core(tv, tofree, numbuf, copyID, TRUE, FALSE, FALSE); +} + /* * Return a string with the string representation of a variable. * If the memory is allocated "tofree" is set to it, otherwise NULL. @@ -8184,31 +8246,7 @@ tv2string( char_u *numbuf, int copyID) { - switch (tv->v_type) - { - case VAR_FUNC: - *tofree = string_quote(tv->vval.v_string, TRUE); - return *tofree; - case VAR_STRING: - *tofree = string_quote(tv->vval.v_string, FALSE); - return *tofree; - case VAR_FLOAT: -#ifdef FEAT_FLOAT - *tofree = NULL; - vim_snprintf((char *)numbuf, NUMBUFLEN - 1, "%g", tv->vval.v_float); - return numbuf; -#endif - case VAR_NUMBER: - case VAR_LIST: - case VAR_DICT: - case VAR_PARTIAL: - case VAR_SPECIAL: - case VAR_JOB: - case VAR_CHANNEL: - case VAR_UNKNOWN: - break; - } - return echo_string(tv, tofree, numbuf, copyID); + return echo_string_core(tv, tofree, numbuf, copyID, FALSE, TRUE, FALSE); } /* @@ -15197,7 +15235,7 @@ f_join(typval_T *argvars, typval_T *rettv) if (sep != NULL) { ga_init2(&ga, (int)sizeof(char), 80); - list_join(&ga, argvars[0].vval.v_list, sep, TRUE, 0); + list_join(&ga, argvars[0].vval.v_list, sep, TRUE, FALSE, 0); ga_append(&ga, NUL); rettv->vval.v_string = (char_u *)ga.ga_data; } diff --git a/src/ex_cmds2.c b/src/ex_cmds2.c index ad613ed34f..e617e37801 100644 --- a/src/ex_cmds2.c +++ b/src/ex_cmds2.c @@ -1252,6 +1252,25 @@ stop_timer(timer_T *timer) remove_timer(timer); free_timer(timer); } + +/* + * Mark references in partials of timers. + */ + int +set_ref_in_timer(int copyID) +{ + int abort = FALSE; + timer_T *timer; + typval_T tv; + + for (timer = first_timer; timer != NULL; timer = timer->tr_next) + { + tv.v_type = VAR_PARTIAL; + tv.vval.v_partial = timer->tr_partial; + abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL); + } + return abort; +} # endif #if defined(FEAT_SYN_HL) && defined(FEAT_RELTIME) && defined(FEAT_FLOAT) @@ -3397,10 +3416,10 @@ add_pack_plugin(char_u *fname, void *cookie) int c; char_u *new_rtp; int keep; - int oldlen; - int addlen; + size_t oldlen; + size_t addlen; char_u *afterdir; - int afterlen = 0; + size_t afterlen = 0; char_u *ffname = fix_fname(fname); if (ffname == NULL) @@ -3440,9 +3459,9 @@ add_pack_plugin(char_u *fname, void *cookie) if (afterdir != NULL && mch_isdir(afterdir)) afterlen = STRLEN(afterdir) + 1; /* add one for comma */ - oldlen = (int)STRLEN(p_rtp); - addlen = (int)STRLEN(ffname) + 1; /* add one for comma */ - new_rtp = alloc(oldlen + addlen + afterlen + 1); /* add one for NUL */ + oldlen = STRLEN(p_rtp); + addlen = STRLEN(ffname) + 1; /* add one for comma */ + new_rtp = alloc((int)(oldlen + addlen + afterlen + 1)); /* add one for NUL */ if (new_rtp == NULL) goto theend; keep = (int)(insp - p_rtp); diff --git a/src/misc2.c b/src/misc2.c index 0dc61a5440..eccb35574d 100644 --- a/src/misc2.c +++ b/src/misc2.c @@ -1126,9 +1126,6 @@ free_all_mem(void) # endif # ifdef FEAT_DIFF diff_clear(curtab); -# endif -# ifdef FEAT_JOB_CHANNEL - channel_free_all(); # endif clear_sb_text(); /* free any scrollback text */ @@ -1221,6 +1218,10 @@ free_all_mem(void) # ifdef FEAT_EVAL eval_clear(); # endif +# ifdef FEAT_JOB_CHANNEL + channel_free_all(); + job_free_all(); +# endif free_termoptions(); diff --git a/src/proto/channel.pro b/src/proto/channel.pro index 64e55d60a3..65b266fec8 100644 --- a/src/proto/channel.pro +++ b/src/proto/channel.pro @@ -50,6 +50,7 @@ void clear_job_options(jobopt_T *opt); void free_job_options(jobopt_T *opt); int get_job_options(typval_T *tv, jobopt_T *opt, int supported); channel_T *get_channel_arg(typval_T *tv, int check_open, int reading, int part); +void job_free_all(void); int set_ref_in_job(int copyID); void job_unref(job_T *job); int free_unused_jobs_contents(int copyID, int mask); diff --git a/src/proto/ex_cmds2.pro b/src/proto/ex_cmds2.pro index 5e5b4d4b73..8d9b72a248 100644 --- a/src/proto/ex_cmds2.pro +++ b/src/proto/ex_cmds2.pro @@ -22,6 +22,7 @@ timer_T *create_timer(long msec, int repeats); long check_due_timer(void); timer_T *find_timer(int id); void stop_timer(timer_T *timer); +int set_ref_in_timer(int copyID); void profile_divide(proftime_T *tm, int count, proftime_T *tm2); void profile_add(proftime_T *tm, proftime_T *tm2); void profile_self(proftime_T *self, proftime_T *total, proftime_T *children); diff --git a/src/structs.h b/src/structs.h index 8f07320227..14eea7de32 100644 --- a/src/structs.h +++ b/src/structs.h @@ -1404,6 +1404,8 @@ typedef struct { partial_T *ch_partial; buf_T *ch_buffer; /* buffer to read from or write to */ + int ch_nomodifiable; /* TRUE when buffer can be 'nomodifiable' */ + int ch_nomod_error; /* TRUE when e_modifiable was given */ int ch_buf_append; /* write appended lines instead top-bot */ linenr_T ch_buf_top; /* next line to send */ linenr_T ch_buf_bot; /* last line to send */ @@ -1480,6 +1482,8 @@ struct channel_S { #define JO_IN_BUF 0x4000000 /* "in_buf" (JO_OUT_BUF << 2) */ #define JO_CHANNEL 0x8000000 /* "channel" */ #define JO_BLOCK_WRITE 0x10000000 /* "block_write" */ +#define JO_OUT_MODIFIABLE 0x20000000 /* "out_modifiable" */ +#define JO_ERR_MODIFIABLE 0x40000000 /* "err_modifiable" (JO_OUT_ << 1) */ #define JO_ALL 0x7fffffff #define JO_MODE_ALL (JO_MODE + JO_IN_MODE + JO_OUT_MODE + JO_ERR_MODE) @@ -1503,6 +1507,7 @@ typedef struct char_u jo_io_name_buf[4][NUMBUFLEN]; char_u *jo_io_name[4]; /* not allocated! */ int jo_io_buf[4]; + int jo_modifiable[4]; channel_T *jo_channel; linenr_T jo_in_top; @@ -1527,7 +1532,6 @@ typedef struct int jo_id; char_u jo_soe_buf[NUMBUFLEN]; char_u *jo_stoponexit; - char_u jo_ecb_buf[NUMBUFLEN]; } jobopt_T; diff --git a/src/syntax.c b/src/syntax.c index 5bd88c57a8..a9c15335ab 100644 --- a/src/syntax.c +++ b/src/syntax.c @@ -8487,11 +8487,11 @@ color_name2handle(char_u *name) #ifdef FEAT_GUI return gui.norm_pixel; #endif -#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI) - else -#endif #ifdef FEAT_TERMGUICOLORS + if (cterm_normal_fg_gui_color != (long_u)INVALCOLOR) return cterm_normal_fg_gui_color; + /* Guess that the foreground is black or white. */ + return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white")); #endif } if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0) @@ -8502,11 +8502,11 @@ color_name2handle(char_u *name) #ifdef FEAT_GUI return gui.back_pixel; #endif -#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI) - else -#endif #ifdef FEAT_TERMGUICOLORS + if (cterm_normal_bg_gui_color != (long_u)INVALCOLOR) return cterm_normal_bg_gui_color; + /* Guess that the background is white or black. */ + return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black")); #endif } @@ -8596,7 +8596,6 @@ get_attr_entry(garray_T *table, attrentry_T *aep) && aep->ae_u.cterm.bg_rgb == taep->ae_u.cterm.bg_rgb #endif - ))) return i + ATTR_OFF; diff --git a/src/testdir/Makefile b/src/testdir/Makefile index 49a817ab1c..129ecaf6c1 100644 --- a/src/testdir/Makefile +++ b/src/testdir/Makefile @@ -12,7 +12,7 @@ SCRIPTSOURCE = ../../runtime # The output goes into a file "valgrind.testN" # Vim should be compiled with EXITFREE to avoid false warnings. # This will make testing about 10 times as slow. -# VALGRIND = valgrind --tool=memcheck --leak-check=yes --num-callers=15 --log-file=valgrind.$* +# VALGRIND = valgrind --tool=memcheck --leak-check=yes --num-callers=25 --log-file=valgrind.$* default: nongui @@ -52,7 +52,7 @@ $(SCRIPTS) $(SCRIPTS_GUI) $(NEW_TESTS): $(SCRIPTS_FIRST) RM_ON_RUN = test.out X* viminfo RM_ON_START = tiny.vim small.vim mbyte.vim mzscheme.vim lua.vim test.ok benchmark.out -RUN_VIM = VIMRUNTIME=$(SCRIPTSOURCE); export VIMRUNTIME; $(VALGRIND) $(VIMPROG) -f -u unix.vim $(NO_PLUGIN) -s dotest.in +RUN_VIM = VIMRUNTIME=$(SCRIPTSOURCE); export VIMRUNTIME; $(VALGRIND) $(VIMPROG) -f $(GUI_FLAG) -u unix.vim $(NO_PLUGIN) -s dotest.in clean: -rm -rf *.out *.failed *.res *.rej *.orig test.log messages $(RM_ON_RUN) $(RM_ON_START) valgrind.* @@ -118,7 +118,7 @@ nolog: # New style of tests uses Vim script with assert calls. These are easier # to write and a lot easier to read and debug. # Limitation: Only works with the +eval feature. -RUN_VIMTEST = VIMRUNTIME=$(SCRIPTSOURCE); export VIMRUNTIME; $(VALGRIND) $(VIMPROG) -f -u unix.vim $(NO_PLUGIN) +RUN_VIMTEST = VIMRUNTIME=$(SCRIPTSOURCE); export VIMRUNTIME; $(VALGRIND) $(VIMPROG) -f $(GUI_FLAG) -u unix.vim $(NO_PLUGIN) newtests: newtestssilent @/bin/sh -c "if test -f messages && grep -q 'FAILED' messages; then cat messages && cat test.log; fi" diff --git a/src/testdir/test86.ok b/src/testdir/test86.ok index dc9e2a91da..6d561ecd1c 100644 --- a/src/testdir/test86.ok +++ b/src/testdir/test86.ok @@ -484,7 +484,7 @@ psa9: psaB: psaC: -psar: +psar: s(a): function('Args') s(pa1): function('Args', ['abcArgsPA1']) s(pa2): function('Args') diff --git a/src/testdir/test87.ok b/src/testdir/test87.ok index b36727958f..34e9279633 100644 --- a/src/testdir/test87.ok +++ b/src/testdir/test87.ok @@ -484,7 +484,7 @@ psa9: psaB: psaC: -psar: +psar: s(a): function('Args') s(pa1): function('Args', ['abcArgsPA1']) s(pa2): function('Args') diff --git a/src/testdir/test_channel.vim b/src/testdir/test_channel.vim index 363b5cd9e1..ed3fa6d466 100644 --- a/src/testdir/test_channel.vim +++ b/src/testdir/test_channel.vim @@ -676,7 +676,7 @@ func Test_nl_write_both_file() endtry endfunc -func Run_test_pipe_to_buffer(use_name) +func Run_test_pipe_to_buffer(use_name, nomod) if !has('job') return endif @@ -691,6 +691,9 @@ func Run_test_pipe_to_buffer(use_name) quit let firstline = '' endif + if a:nomod + let options['out_modifiable'] = 0 + endif let job = job_start(s:python . " test_channel_pipe.py", options) call assert_equal("run", job_status(job)) try @@ -705,6 +708,11 @@ func Run_test_pipe_to_buffer(use_name) $del endif call assert_equal([firstline, 'line one', 'line two', 'this', 'AND this', 'Goodbye!'], getline(1, '$')) + if a:nomod + call assert_equal(0, &modifiable) + else + call assert_equal(1, &modifiable) + endif bwipe! finally call job_stop(job) @@ -712,14 +720,18 @@ func Run_test_pipe_to_buffer(use_name) endfunc func Test_pipe_to_buffer_name() - call Run_test_pipe_to_buffer(1) + call Run_test_pipe_to_buffer(1, 0) endfunc func Test_pipe_to_buffer_nr() - call Run_test_pipe_to_buffer(0) + call Run_test_pipe_to_buffer(0, 0) endfunc -func Run_test_pipe_err_to_buffer(use_name) +func Test_pipe_to_buffer_name_nomod() + call Run_test_pipe_to_buffer(1, 1) +endfunc + +func Run_test_pipe_err_to_buffer(use_name, nomod) if !has('job') return endif @@ -734,6 +746,9 @@ func Run_test_pipe_err_to_buffer(use_name) quit let firstline = '' endif + if a:nomod + let options['err_modifiable'] = 0 + endif let job = job_start(s:python . " test_channel_pipe.py", options) call assert_equal("run", job_status(job)) try @@ -745,6 +760,11 @@ func Run_test_pipe_err_to_buffer(use_name) sp pipe-err call s:waitFor('line("$") >= 5') call assert_equal([firstline, 'line one', 'line two', 'this', 'AND this'], getline(1, '$')) + if a:nomod + call assert_equal(0, &modifiable) + else + call assert_equal(1, &modifiable) + endif bwipe! finally call job_stop(job) @@ -752,11 +772,15 @@ func Run_test_pipe_err_to_buffer(use_name) endfunc func Test_pipe_err_to_buffer_name() - call Run_test_pipe_err_to_buffer(1) + call Run_test_pipe_err_to_buffer(1, 0) endfunc func Test_pipe_err_to_buffer_nr() - call Run_test_pipe_err_to_buffer(0) + call Run_test_pipe_err_to_buffer(0, 0) +endfunc + +func Test_pipe_err_to_buffer_name_nomod() + call Run_test_pipe_err_to_buffer(1, 1) endfunc func Test_pipe_both_to_buffer() diff --git a/src/testdir/test_partial.vim b/src/testdir/test_partial.vim index af6ca6db25..9cbb7a3655 100644 --- a/src/testdir/test_partial.vim +++ b/src/testdir/test_partial.vim @@ -250,6 +250,25 @@ func Test_cycle_partial_job() endif endfunc +func Test_job_start_fails() + if has('job') + let job = job_start('axdfxsdf') + for i in range(100) + let status = job_status(job) + if status == 'dead' || status == 'fail' + break + endif + sleep 10m + endfor + if has('unix') + call assert_equal('dead', job_status(job)) + else + call assert_equal('fail', job_status(job)) + endif + unlet job + endif +endfunc + func Test_ref_job_partial_dict() if has('job') let g:ref_job = job_start('echo') diff --git a/src/testdir/test_syn_attr.vim b/src/testdir/test_syn_attr.vim index 51674d0ee2..fe6acaf6e5 100644 --- a/src/testdir/test_syn_attr.vim +++ b/src/testdir/test_syn_attr.vim @@ -27,7 +27,7 @@ func Test_missing_attr() if fontname == '' let fontname = 'something' endif - exe 'hi Mine guifg=blue guibg=red font=' . escape(fontname, ' \') + exe "hi Mine guifg=blue guibg=red font='" . fontname . "'" call assert_equal('blue', synIDattr(hlID("Mine"), "fg", 'gui')) call assert_equal('red', synIDattr(hlID("Mine"), "bg", 'gui')) call assert_equal(fontname, synIDattr(hlID("Mine"), "font", 'gui')) diff --git a/src/testdir/test_timers.vim b/src/testdir/test_timers.vim index 7ef51e5b5d..0969377c87 100644 --- a/src/testdir/test_timers.vim +++ b/src/testdir/test_timers.vim @@ -8,6 +8,10 @@ func MyHandler(timer) let s:val += 1 endfunc +func MyHandlerWithLists(lists, timer) + let x = string(a:lists) +endfunc + func Test_oneshot() let s:val = 0 let timer = timer_start(50, 'MyHandler') @@ -42,4 +46,10 @@ func Test_with_partial_callback() sleep 200m call assert_equal(1, s:val) endfunc + +func Test_retain_partial() + call timer_start(100, function('MyHandlerWithLists', [['a']])) + call test_garbagecollect_now() + sleep 200m +endfunc " vim: ts=2 sw=0 et diff --git a/src/testdir/test_viml.vim b/src/testdir/test_viml.vim index 10869f36a9..f1dde4b93d 100644 --- a/src/testdir/test_viml.vim +++ b/src/testdir/test_viml.vim @@ -1052,6 +1052,150 @@ func Test_skip() endfunc +"------------------------------------------------------------------------------- +" Test 93: :echo and string() {{{1 +"------------------------------------------------------------------------------- + +func Test_echo_and_string() + " String + let a = 'foo bar' + redir => result + echo a + echo string(a) + redir END + let l = split(result, "\n") + call assert_equal(["foo bar", + \ "'foo bar'"], l) + + " Float + if has('float') + let a = -1.2e0 + redir => result + echo a + echo string(a) + redir END + let l = split(result, "\n") + call assert_equal(["-1.2", + \ "-1.2"], l) + endif + + " Funcref + redir => result + echo function('string') + echo string(function('string')) + redir END + let l = split(result, "\n") + call assert_equal(["string", + \ "function('string')"], l) + + " Recursive dictionary + let a = {} + let a["a"] = a + redir => result + echo a + echo string(a) + redir END + let l = split(result, "\n") + call assert_equal(["{'a': {...}}", + \ "{'a': {...}}"], l) + + " Recursive list + let a = [0] + let a[0] = a + redir => result + echo a + echo string(a) + redir END + let l = split(result, "\n") + call assert_equal(["[[...]]", + \ "[[...]]"], l) + + " Empty dictionaries in a list + let a = {} + redir => result + echo [a, a, a] + echo string([a, a, a]) + redir END + let l = split(result, "\n") + call assert_equal(["[{}, {}, {}]", + \ "[{}, {}, {}]"], l) + + " Empty dictionaries in a dictionary + let a = {} + let b = {"a": a, "b": a} + redir => result + echo b + echo string(b) + redir END + let l = split(result, "\n") + call assert_equal(["{'a': {}, 'b': {}}", + \ "{'a': {}, 'b': {}}"], l) + + " Empty lists in a list + let a = [] + redir => result + echo [a, a, a] + echo string([a, a, a]) + redir END + let l = split(result, "\n") + call assert_equal(["[[], [], []]", + \ "[[], [], []]"], l) + + " Empty lists in a dictionary + let a = [] + let b = {"a": a, "b": a} + redir => result + echo b + echo string(b) + redir END + let l = split(result, "\n") + call assert_equal(["{'a': [], 'b': []}", + \ "{'a': [], 'b': []}"], l) + + " Dictionaries in a list + let a = {"one": "yes", "two": "yes", "three": "yes"} + redir => result + echo [a, a, a] + echo string([a, a, a]) + redir END + let l = split(result, "\n") + call assert_equal(["[{'one': 'yes', 'two': 'yes', 'three': 'yes'}, {...}, {...}]", + \ "[{'one': 'yes', 'two': 'yes', 'three': 'yes'}, {'one': 'yes', 'two': 'yes', 'three': 'yes'}, {'one': 'yes', 'two': 'yes', 'three': 'yes'}]"], l) + + " Dictionaries in a dictionary + let a = {"one": "yes", "two": "yes", "three": "yes"} + let b = {"a": a, "b": a} + redir => result + echo b + echo string(b) + redir END + let l = split(result, "\n") + call assert_equal(["{'a': {'one': 'yes', 'two': 'yes', 'three': 'yes'}, 'b': {...}}", + \ "{'a': {'one': 'yes', 'two': 'yes', 'three': 'yes'}, 'b': {'one': 'yes', 'two': 'yes', 'three': 'yes'}}"], l) + + " Lists in a list + let a = [1, 2, 3] + redir => result + echo [a, a, a] + echo string([a, a, a]) + redir END + let l = split(result, "\n") + call assert_equal(["[[1, 2, 3], [...], [...]]", + \ "[[1, 2, 3], [1, 2, 3], [1, 2, 3]]"], l) + + " Lists in a dictionary + let a = [1, 2, 3] + let b = {"a": a, "b": a} + redir => result + echo b + echo string(b) + redir END + let l = split(result, "\n") + call assert_equal(["{'a': [1, 2, 3], 'b': [...]}", + \ "{'a': [1, 2, 3], 'b': [1, 2, 3]}"], l) + +endfunc + "------------------------------------------------------------------------------- " Modelines {{{1 " vim: ts=8 sw=4 tw=80 fdm=marker diff --git a/src/version.c b/src/version.c index 4fd2ec2990..84457b84b9 100644 --- a/src/version.c +++ b/src/version.c @@ -768,6 +768,32 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1862, +/**/ + 1861, +/**/ + 1860, +/**/ + 1859, +/**/ + 1858, +/**/ + 1857, +/**/ + 1856, +/**/ + 1855, +/**/ + 1854, +/**/ + 1853, +/**/ + 1852, +/**/ + 1851, +/**/ + 1850, /**/ 1849, /**/ diff --git a/src/vim.h b/src/vim.h index 877d6f7ea3..fd5d65e06a 100644 --- a/src/vim.h +++ b/src/vim.h @@ -910,9 +910,10 @@ extern char *(*dyn_libintl_textdomain)(const char *domainname); #define GETF_SWITCH 0x04 /* respect 'switchbuf' settings when jumping */ /* Values for buflist_new() flags */ -#define BLN_CURBUF 1 /* May re-use curbuf for new buffer */ -#define BLN_LISTED 2 /* Put new buffer in buffer list */ -#define BLN_DUMMY 4 /* Allocating dummy buffer */ +#define BLN_CURBUF 1 /* may re-use curbuf for new buffer */ +#define BLN_LISTED 2 /* put new buffer in buffer list */ +#define BLN_DUMMY 4 /* allocating dummy buffer */ +#define BLN_NEW 8 /* create a new buffer */ /* Values for in_cinkeys() */ #define KEY_OPEN_FORW 0x101