From bf981eeb6b4ee63ae8543a7f9865ab700159a79c Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 28 May 2016 13:20:31 +0200 Subject: [PATCH 01/13] patch 7.4.1850 Problem: GUI freezes when using a job. (Shougo) Solution: Unregister the channel when there is an input error. --- src/channel.c | 5 +++++ src/version.c | 2 ++ 2 files changed, 7 insertions(+) diff --git a/src/channel.c b/src/channel.c index 84dfcf7c0b..f4dc323b6f 100644 --- a/src/channel.c +++ b/src/channel.c @@ -2868,6 +2868,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 diff --git a/src/version.c b/src/version.c index 2a259a528e..80793f10bb 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1850, /**/ 1849, /**/ From 180fc2d41812c49b60224a1ca89945a002a090f5 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 28 May 2016 13:28:10 +0200 Subject: [PATCH 02/13] patch 7.4.1851 Problem: test_syn_attr failes when using the GUI. (Dominique Pelle) Solution: Escape the font name properly. --- src/testdir/test_syn_attr.vim | 2 +- src/version.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) 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/version.c b/src/version.c index 80793f10bb..73e08fafab 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1851, /**/ 1850, /**/ From af6c12c27bcb553b2fb2c8a9dcfde626fb3670fe Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 28 May 2016 13:40:10 +0200 Subject: [PATCH 03/13] patch 7.4.1852 Problem: Unix: Cannot run all tests with the GUI. Solution: Add the "testgui" target. --- src/Makefile | 7 ++++++- src/testdir/Makefile | 6 +++--- src/version.c | 2 ++ 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/Makefile b/src/Makefile index b3b8daf0b1..5b510e0ebd 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 @@ -1943,6 +1943,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) @@ -2057,6 +2061,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/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/version.c b/src/version.c index 73e08fafab..b50b19eb67 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1852, /**/ 1851, /**/ From 28ae5773422c2cf61aaf8d9d2b9fae70642d6a33 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 28 May 2016 14:16:10 +0200 Subject: [PATCH 04/13] patch 7.4.1853 Problem: Crash when job and channel are in the same dict while using partials. (Luc Hermitte) Solution: Do not decrement the channel reference count too early. --- src/channel.c | 3 ++- src/version.c | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/channel.c b/src/channel.c index f4dc323b6f..94681ee898 100644 --- a/src/channel.c +++ b/src/channel.c @@ -2566,7 +2566,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); @@ -2574,6 +2573,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; diff --git a/src/version.c b/src/version.c index b50b19eb67..83221e23bd 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1853, /**/ 1852, /**/ From d80629cef03cd40b0bf06c402dfe0b720b3bf608 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 28 May 2016 15:53:53 +0200 Subject: [PATCH 05/13] patch 7.4.1854 Problem: When setting 'termguicolors' the Ignore highlighting doesn't work. (Charles Campbell) Solution: Handle the color names "fg" and "bg" when the GUI isn't running and no colors are speficied, fall back to black and white. --- src/syntax.c | 13 ++++++------- src/version.c | 2 ++ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/syntax.c b/src/syntax.c index e68090db69..0face62b7f 100644 --- a/src/syntax.c +++ b/src/syntax.c @@ -8486,11 +8486,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) @@ -8501,11 +8501,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 } @@ -8595,7 +8595,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/version.c b/src/version.c index 83221e23bd..2ec25c0b5e 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1854, /**/ 1853, /**/ From 655da31a18ef3f888acf10e68b438e2a851f7b14 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 28 May 2016 22:22:34 +0200 Subject: [PATCH 06/13] patch 7.4.1855 Problem: Valgrind reports memory leak for job that is not freed. Solution: Free all jobs on exit. Add test for failing job. --- src/channel.c | 12 +++++++++++- src/misc2.c | 7 ++++--- src/proto/channel.pro | 1 + src/testdir/test_partial.vim | 14 ++++++++++++++ src/version.c | 2 ++ 5 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/channel.c b/src/channel.c index 94681ee898..2cc46fecb7 100644 --- a/src/channel.c +++ b/src/channel.c @@ -1285,6 +1285,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); @@ -2888,7 +2889,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. */ static void channel_read(channel_T *channel, int part, char *func) @@ -4184,6 +4185,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/misc2.c b/src/misc2.c index a0cce07f66..b4e94795f1 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 5dc512181f..60b68f0133 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/testdir/test_partial.vim b/src/testdir/test_partial.vim index af6ca6db25..f33aab3a1a 100644 --- a/src/testdir/test_partial.vim +++ b/src/testdir/test_partial.vim @@ -250,6 +250,20 @@ 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) + if job_status(job) == 'dead' + break + endif + sleep 10m + endfor + call assert_equal('dead', job_status(job)) + unlet job + endif +endfunc + func Test_ref_job_partial_dict() if has('job') let g:ref_job = job_start('echo') diff --git a/src/version.c b/src/version.c index 2ec25c0b5e..e68c4ec05f 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1855, /**/ 1854, /**/ From 324a78f3b649e7b14741519ecf19c4aba178772d Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 28 May 2016 22:47:12 +0200 Subject: [PATCH 07/13] patch 7.4.1856 Problem: failing job test fails on MS-Windows. Solution: Expect "fail" status instead of "dead". --- src/testdir/test_partial.vim | 9 +++++++-- src/version.c | 2 ++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/testdir/test_partial.vim b/src/testdir/test_partial.vim index f33aab3a1a..9cbb7a3655 100644 --- a/src/testdir/test_partial.vim +++ b/src/testdir/test_partial.vim @@ -254,12 +254,17 @@ func Test_job_start_fails() if has('job') let job = job_start('axdfxsdf') for i in range(100) - if job_status(job) == 'dead' + let status = job_status(job) + if status == 'dead' || status == 'fail' break endif sleep 10m endfor - call assert_equal('dead', job_status(job)) + if has('unix') + call assert_equal('dead', job_status(job)) + else + call assert_equal('fail', job_status(job)) + endif unlet job endif endfunc diff --git a/src/version.c b/src/version.c index e68c4ec05f..e47ab53387 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1856, /**/ 1855, /**/ From 9f5842e63fc63d438cbffcec503e072a06f74dc2 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 29 May 2016 16:17:08 +0200 Subject: [PATCH 08/13] patch 7.4.1857 Problem: When a channel appends to a buffer that is 'nomodifiable' there is an error but appending is done anyway. Solution: Add the 'modifiable' option. Refuse to write to a 'nomodifiable' when the value is 1. --- runtime/doc/channel.txt | 23 +++++++++++--- src/channel.c | 60 ++++++++++++++++++++++++++++++++---- src/structs.h | 5 +++ src/testdir/test_channel.vim | 36 ++++++++++++++++++---- src/version.c | 2 ++ 5 files changed, 109 insertions(+), 17 deletions(-) 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/channel.c b/src/channel.c index 2cc46fecb7..973d234eac 100644 --- a/src/channel.c +++ b/src/channel.c @@ -1209,9 +1209,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; + } } } @@ -1236,9 +1247,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; + } } } @@ -2107,11 +2128,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. */ @@ -2124,6 +2157,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 */ @@ -2132,6 +2166,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) { @@ -2359,7 +2397,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) @@ -3915,6 +3953,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) { diff --git a/src/structs.h b/src/structs.h index 12a8a43878..d5f8adc83a 100644 --- a/src/structs.h +++ b/src/structs.h @@ -1401,6 +1401,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 */ @@ -1477,6 +1479,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) @@ -1500,6 +1504,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; 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/version.c b/src/version.c index e47ab53387..c3afbad85b 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1857, /**/ 1856, /**/ From b127cfd75f59e82580df395b6e2c009774644b16 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 29 May 2016 16:24:50 +0200 Subject: [PATCH 09/13] patch 7.4.1858 Problem: When a channel writes to a buffer it doesn't find a buffer by the short name but re-uses it anyway. Solution: Find buffer also by the short name. --- src/buffer.c | 3 ++- src/channel.c | 6 +++++- src/version.c | 2 ++ src/vim.h | 7 ++++--- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/buffer.c b/src/buffer.c index b0e3a22b50..0bbb1f467a 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -1649,6 +1649,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 */ @@ -1676,7 +1677,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 973d234eac..b1f7f1a605 100644 --- a/src/channel.c +++ b/src/channel.c @@ -1079,11 +1079,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); diff --git a/src/version.c b/src/version.c index c3afbad85b..85f57e58b7 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1858, /**/ 1857, /**/ diff --git a/src/vim.h b/src/vim.h index a5d05a15f8..8ce11d684c 100644 --- a/src/vim.h +++ b/src/vim.h @@ -907,9 +907,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 From ef3abc6442260e9a0314970a532400b05571d3fe Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 29 May 2016 16:44:26 +0200 Subject: [PATCH 10/13] patch 7.4.1859 Problem: Cannot use a function reference for "exit_cb". Solution: Use get_callback(). (Yegappan Lakshmanan) --- src/channel.c | 33 ++++++++++++++------------------- src/structs.h | 1 - src/version.c | 2 ++ 3 files changed, 16 insertions(+), 20 deletions(-) diff --git a/src/channel.c b/src/channel.c index b1f7f1a605..75cadae7ab 100644 --- a/src/channel.c +++ b/src/channel.c @@ -3839,6 +3839,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); } /* @@ -4051,6 +4053,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)) @@ -4113,25 +4127,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)) diff --git a/src/structs.h b/src/structs.h index d5f8adc83a..08d3325431 100644 --- a/src/structs.h +++ b/src/structs.h @@ -1529,7 +1529,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/version.c b/src/version.c index 85f57e58b7..eb9f2c7112 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1859, /**/ 1858, /**/ From e3188e261569ae512fb1ae2653b57fdd9e259ca3 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Tue, 31 May 2016 21:13:04 +0200 Subject: [PATCH 11/13] patch 7.4.1860 Problem: Using a partial for timer_start() may cause a crash. Solution: Set the copyID in timer objects. (Ozaki Kiichi) --- src/eval.c | 4 ++++ src/ex_cmds2.c | 19 +++++++++++++++++++ src/proto/ex_cmds2.pro | 1 + src/testdir/test_timers.vim | 10 ++++++++++ src/version.c | 2 ++ 5 files changed, 36 insertions(+) diff --git a/src/eval.c b/src/eval.c index 3578c9997c..d30a766531 100644 --- a/src/eval.c +++ b/src/eval.c @@ -7046,6 +7046,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) { /* diff --git a/src/ex_cmds2.c b/src/ex_cmds2.c index a90fe7d8c6..9adaea357d 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) 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/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/version.c b/src/version.c index eb9f2c7112..515f518762 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1860, /**/ 1859, /**/ From b055066a1daf12c349d6c575aff22ae4d999a157 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Tue, 31 May 2016 21:37:36 +0200 Subject: [PATCH 12/13] patch 7.4.1861 Problem: Compiler warnings with 64 bit compiler. Solution: Change int to size_t. (Mike William) --- src/ex_cmds2.c | 12 ++++++------ src/version.c | 2 ++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/ex_cmds2.c b/src/ex_cmds2.c index 9adaea357d..5efe0ec15a 100644 --- a/src/ex_cmds2.c +++ b/src/ex_cmds2.c @@ -3343,10 +3343,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) @@ -3386,9 +3386,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/version.c b/src/version.c index 515f518762..bf77c87e95 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1861, /**/ 1860, /**/ From 18dfb4404a618c52ee7138630a2381aed4d66eaf Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Tue, 31 May 2016 22:31:23 +0200 Subject: [PATCH 13/13] patch 7.4.1862 Problem: string() with repeated argument does not give a result usable by eval(). Solution: Refactor echo_striong and tv2string(), moving the common part to echo_string_core(). (Ken Takata) --- src/eval.c | 134 ++++++++++++++++++++++------------- src/testdir/test86.ok | 2 +- src/testdir/test87.ok | 2 +- src/testdir/test_viml.vim | 144 ++++++++++++++++++++++++++++++++++++++ src/version.c | 2 + 5 files changed, 232 insertions(+), 52 deletions(-) diff --git a/src/eval.c b/src/eval.c index d30a766531..adfe8f899a 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) @@ -7833,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; @@ -7868,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); @@ -8026,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; @@ -8057,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: @@ -8114,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; @@ -8133,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: @@ -8174,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. @@ -8188,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); } /* @@ -15182,7 +15216,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/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_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 bf77c87e95..279ee6d396 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1862, /**/ 1861, /**/