summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2018-03-25 18:20:17 +0200
committerBram Moolenaar <Bram@vim.org>2018-03-25 18:20:17 +0200
commit8fbaeb195d9298c3a2a80300b5f96f1adddd2f59 (patch)
tree08e90a8db454c2031636deb5c912ec218dcc3566 /src
parent65873846e088bb94028e9d591ea03e377cb206b5 (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.c2
-rw-r--r--src/terminal.c147
-rw-r--r--src/testdir/screendump.vim4
-rw-r--r--src/testdir/test_terminal.vim76
-rw-r--r--src/version.c2
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,