diff options
author | Bram Moolenaar <Bram@vim.org> | 2018-03-25 18:20:17 +0200 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2018-03-25 18:20:17 +0200 |
commit | 8fbaeb195d9298c3a2a80300b5f96f1adddd2f59 (patch) | |
tree | 08e90a8db454c2031636deb5c912ec218dcc3566 /src | |
parent | 65873846e088bb94028e9d591ea03e377cb206b5 (diff) |
patch 8.0.1641: job in terminal can't communicate with Vimv8.0.1641
Problem: Job in terminal can't communicate with Vim.
Solution: Add the terminal API.
Diffstat (limited to 'src')
-rw-r--r-- | src/buffer.c | 2 | ||||
-rw-r--r-- | src/terminal.c | 147 | ||||
-rw-r--r-- | src/testdir/screendump.vim | 4 | ||||
-rw-r--r-- | src/testdir/test_terminal.vim | 76 | ||||
-rw-r--r-- | src/version.c | 2 |
5 files changed, 222 insertions, 9 deletions
diff --git a/src/buffer.c b/src/buffer.c index c7f361af0a..7bd3cdf016 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -948,7 +948,7 @@ clear_wininfo(buf_T *buf) } } -#if defined(FEAT_LISTCMDS) || defined(PROTO) +#if defined(FEAT_LISTCMDS) || defined(FEAT_TERMINAL) || defined(PROTO) /* * Go to another buffer. Handles the result of the ATTENTION dialog. */ diff --git a/src/terminal.c b/src/terminal.c index 4f29b56699..50e87c9e45 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -38,12 +38,11 @@ * in tl_scrollback are no longer used. * * TODO: - * - Win32: In the GUI use a terminal emulator for :!cmd. + * - For the "drop" command accept another argument for options. * - Add a way to set the 16 ANSI colors, to be used for 'termguicolors' and in * the GUI. - * - Some way for the job running in the terminal to send a :drop command back - * to the Vim running the terminal. Should be usable by a simple shell or - * python script. + * - Win32: Make terminal used for :!cmd in the GUI work better. Allow for + * redirection. * - implement term_setsize() * - Copy text in the vterm to the Vim buffer once in a while, so that * completion works. @@ -3146,6 +3145,140 @@ init_default_colors(term_T *term) } /* + * Handles a "drop" command from the job in the terminal. + * "item" is the file name, "item->li_next" may have options. + */ + static void +handle_drop_command(listitem_T *item) +{ + char_u *fname = get_tv_string(&item->li_tv); + int bufnr; + win_T *wp; + tabpage_T *tp; + exarg_T ea; + + bufnr = buflist_add(fname, BLN_LISTED | BLN_NOOPT); + FOR_ALL_TAB_WINDOWS(tp, wp) + { + if (wp->w_buffer->b_fnum == bufnr) + { + /* buffer is in a window already, go there */ + goto_tabpage_win(tp, wp); + return; + } + } + + /* open in new window, like ":sbuffer N" */ + vim_memset(&ea, 0, sizeof(ea)); + ea.cmd = (char_u *)"sbuffer"; + goto_buffer(&ea, DOBUF_FIRST, FORWARD, bufnr); +} + +/* + * Handles a function call from the job running in a terminal. + * "item" is the function name, "item->li_next" has the arguments. + */ + static void +handle_call_command(term_T *term, channel_T *channel, listitem_T *item) +{ + char_u *func; + typval_T argvars[2]; + typval_T rettv; + int doesrange; + + if (item->li_next == NULL) + { + ch_log(channel, "Missing function arguments for call"); + return; + } + func = get_tv_string(&item->li_tv); + + if (!ASCII_ISUPPER(*func)) + { + ch_log(channel, "Invalid function name: %s", func); + return; + } + + argvars[0].v_type = VAR_NUMBER; + argvars[0].vval.v_number = term->tl_buffer->b_fnum; + argvars[1] = item->li_next->li_tv; + if (call_func(func, STRLEN(func), &rettv, + 2, argvars, /* argv_func */ NULL, + /* firstline */ 1, /* lastline */ 1, + &doesrange, /* evaluate */ TRUE, + /* partial */ NULL, /* selfdict */ NULL) == OK) + { + clear_tv(&rettv); + ch_log(channel, "Function %s called", func); + } + else + ch_log(channel, "Calling function %s failed", func); +} + +/* + * Called by libvterm when it cannot recognize an OSC sequence. + * We recognize a terminal API command. + */ + static int +parse_osc(const char *command, size_t cmdlen, void *user) +{ + term_T *term = (term_T *)user; + js_read_T reader; + typval_T tv; + channel_T *channel = term->tl_job == NULL ? NULL + : term->tl_job->jv_channel; + + /* We recognize only OSC 5 1 ; {command} */ + if (cmdlen < 3 || STRNCMP(command, "51;", 3) != 0) + return 0; /* not handled */ + + reader.js_buf = vim_strnsave((char_u *)command + 3, cmdlen - 3); + if (reader.js_buf == NULL) + return 1; + reader.js_fill = NULL; + reader.js_used = 0; + if (json_decode(&reader, &tv, 0) == OK + && tv.v_type == VAR_LIST + && tv.vval.v_list != NULL) + { + listitem_T *item = tv.vval.v_list->lv_first; + + if (item == NULL) + ch_log(channel, "Missing command"); + else + { + char_u *cmd = get_tv_string(&item->li_tv); + + item = item->li_next; + if (item == NULL) + ch_log(channel, "Missing argument for %s", cmd); + else if (STRCMP(cmd, "drop") == 0) + handle_drop_command(item); + else if (STRCMP(cmd, "call") == 0) + handle_call_command(term, channel, item); + else + ch_log(channel, "Invalid command received: %s", cmd); + } + } + else + ch_log(channel, "Invalid JSON received"); + + vim_free(reader.js_buf); + clear_tv(&tv); + return 1; +} + +static VTermParserCallbacks parser_fallbacks = { + NULL, /* text */ + NULL, /* control */ + NULL, /* escape */ + NULL, /* csi */ + parse_osc, /* osc */ + NULL, /* dcs */ + NULL /* resize */ +}; + +/* * Create a new vterm and initialize it. */ static void @@ -3153,6 +3286,7 @@ create_vterm(term_T *term, int rows, int cols) { VTerm *vterm; VTermScreen *screen; + VTermState *state; VTermValue value; vterm = vterm_new(rows, cols); @@ -3186,8 +3320,9 @@ create_vterm(term_T *term, int rows, int cols) #else value.boolean = 0; #endif - vterm_state_set_termprop(vterm_obtain_state(vterm), - VTERM_PROP_CURSORBLINK, &value); + state = vterm_obtain_state(vterm); + vterm_state_set_termprop(state, VTERM_PROP_CURSORBLINK, &value); + vterm_state_set_unrecognised_fallbacks(state, &parser_fallbacks, term); } /* diff --git a/src/testdir/screendump.vim b/src/testdir/screendump.vim index c9b8085dea..093b483f10 100644 --- a/src/testdir/screendump.vim +++ b/src/testdir/screendump.vim @@ -38,8 +38,8 @@ func RunVimInTerminal(arguments, options) endif " Make a horizontal and vertical split, so that we can get exactly the right - " size terminal window. Works only when we currently have one window. - call assert_equal(1, winnr('$')) + " size terminal window. Works only when the current window is full width. + call assert_equal(&columns, winwidth(0)) split vsplit diff --git a/src/testdir/test_terminal.vim b/src/testdir/test_terminal.vim index f2fbaf4676..a7e5f61683 100644 --- a/src/testdir/test_terminal.vim +++ b/src/testdir/test_terminal.vim @@ -1023,3 +1023,79 @@ func Test_terminal_dumpdiff_options() set laststatus& endfunc + +func Test_terminal_api_drop_newwin() + if !CanRunVimInTerminal() + return + endif + call assert_equal(1, winnr('$')) + + " Use the title termcap entries to output the escape sequence. + call writefile([ + \ 'exe "set t_ts=\<Esc>]51; t_fs=\x07"', + \ 'let &titlestring = ''["drop","Xtextfile"]''', + \ 'redraw', + \ "set t_ts=", + \ ], 'Xscript') + let buf = RunVimInTerminal('-S Xscript', {}) + call WaitFor({-> bufnr('Xtextfile') > 0}) + call assert_equal('Xtextfile', expand('%:t')) + call assert_true(winnr('$') >= 3) + + call StopVimInTerminal(buf) + call delete('Xscript') + bwipe Xtextfile +endfunc + +func Test_terminal_api_drop_oldwin() + if !CanRunVimInTerminal() + return + endif + let firstwinid = win_getid() + split Xtextfile + let textfile_winid = win_getid() + call assert_equal(2, winnr('$')) + call win_gotoid(firstwinid) + + " Use the title termcap entries to output the escape sequence. + call writefile([ + \ 'exe "set t_ts=\<Esc>]51; t_fs=\x07"', + \ 'let &titlestring = ''["drop","Xtextfile"]''', + \ 'redraw', + \ "set t_ts=", + \ ], 'Xscript') + let buf = RunVimInTerminal('-S Xscript', {}) + call WaitFor({-> expand('%:t') =='Xtextfile'}) + call assert_equal(textfile_winid, win_getid()) + + call StopVimInTerminal(buf) + call delete('Xscript') + bwipe Xtextfile +endfunc + +func TryThis(bufnum, arg) + let g:called_bufnum = a:bufnum + let g:called_arg = a:arg +endfunc + +func Test_terminal_api_call() + if !CanRunVimInTerminal() + return + endif + " Use the title termcap entries to output the escape sequence. + call writefile([ + \ 'exe "set t_ts=\<Esc>]51; t_fs=\x07"', + \ 'let &titlestring = ''["call","TryThis",["hello",123]]''', + \ 'redraw', + \ "set t_ts=", + \ ], 'Xscript') + let buf = RunVimInTerminal('-S Xscript', {}) + call WaitFor({-> exists('g:called_bufnum')}) + call assert_equal(buf, g:called_bufnum) + call assert_equal(['hello', 123], g:called_arg) + + call StopVimInTerminal(buf) + call delete('Xscript') + unlet g:called_bufnum + unlet g:called_arg +endfunc diff --git a/src/version.c b/src/version.c index d4631ced10..3295eb0892 100644 --- a/src/version.c +++ b/src/version.c @@ -767,6 +767,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1641, +/**/ 1640, /**/ 1639, |