# HG changeset patch # User Christian Brabandt # Date 1464532206 -7200 # Node ID 053bc64433ece5c387b52e9b18560412a5cbdcbb # Parent 23a544d1106405c39e45ea756a170cf60334648f commit https://github.com/vim/vim/commit/9f5842e63fc63d438cbffcec503e072a06f74dc2 Author: Bram Moolenaar Date: Sun May 29 16:17:08 2016 +0200 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. diff --git a/runtime/doc/channel.txt b/runtime/doc/channel.txt --- 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_setoption "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_setoption "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 --- a/src/channel.c +++ b/src/channel.c @@ -1209,9 +1209,20 @@ channel_set_options(channel_T *channel, } 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, 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 * /* 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 * 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, /* 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 * 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 --- 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 --- 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_nam 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_nam 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_nam 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 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1857, +/**/ 1856, /**/ 1855,