1
0
forked from aniani/vim

patch 8.0.1609: shell commands in the GUI use a dumb terminal

Problem:    Shell commands in the GUI use a dumb terminal.
Solution:   Add the "!" flag to 'guioptions' to execute system commands in a
            special terminal window.  Only for Unix now.
This commit is contained in:
Bram Moolenaar
2018-03-16 20:46:58 +01:00
parent 43cb626214
commit 135682517b
10 changed files with 416 additions and 164 deletions

View File

@@ -3845,7 +3845,14 @@ A jump table for the options with a short description can be found at |Q_op|.
To avoid problems with flags that are added in the future, use the To avoid problems with flags that are added in the future, use the
"+=" and "-=" feature of ":set" |add-option-flags|. "+=" and "-=" feature of ":set" |add-option-flags|.
Valid letters are as follows: Valid characters are as follows:
*'go-!'*
'!' External commands are executed in a terminal window. Without
this flag the MS-Windows GUI will open a console window to
execute the command. The Unix GUI will simulate a dumb
terminal to list the command output.
The terminal window will be positioned at the bottom, and grow
upwards as needed.
*guioptions_a* *'go-a'* *guioptions_a* *'go-a'*
'a' Autoselect: If present, then whenever VISUAL mode is started, 'a' Autoselect: If present, then whenever VISUAL mode is started,
or the Visual area extended, Vim tries to become the owner of or the Visual area extended, Vim tries to become the owner of

View File

@@ -5383,11 +5383,13 @@ job_check_ended(void)
/* /*
* Create a job and return it. Implements job_start(). * Create a job and return it. Implements job_start().
* "argv_arg" is only for Unix.
* When "argv_arg" is NULL then "argvars" is used.
* The returned job has a refcount of one. * The returned job has a refcount of one.
* Returns NULL when out of memory. * Returns NULL when out of memory.
*/ */
job_T * job_T *
job_start(typval_T *argvars, jobopt_T *opt_arg) job_start(typval_T *argvars, char **argv_arg, jobopt_T *opt_arg)
{ {
job_T *job; job_T *job;
char_u *cmd = NULL; char_u *cmd = NULL;
@@ -5474,6 +5476,13 @@ job_start(typval_T *argvars, jobopt_T *opt_arg)
job_set_options(job, &opt); job_set_options(job, &opt);
#ifdef USE_ARGV
if (argv_arg != NULL)
{
argv = argv_arg;
}
else
#endif
if (argvars[0].v_type == VAR_STRING) if (argvars[0].v_type == VAR_STRING)
{ {
/* Command is a string. */ /* Command is a string. */
@@ -5551,7 +5560,8 @@ job_start(typval_T *argvars, jobopt_T *opt_arg)
theend: theend:
#ifdef USE_ARGV #ifdef USE_ARGV
vim_free(argv); if (argv != argv_arg)
vim_free(argv);
#else #else
vim_free(ga.ga_data); vim_free(ga.ga_data);
#endif #endif

View File

@@ -7032,7 +7032,7 @@ f_job_start(typval_T *argvars, typval_T *rettv)
rettv->v_type = VAR_JOB; rettv->v_type = VAR_JOB;
if (check_restricted() || check_secure()) if (check_restricted() || check_secure())
return; return;
rettv->vval.v_job = job_start(argvars, NULL); rettv->vval.v_job = job_start(argvars, NULL, NULL);
} }
/* /*

View File

@@ -214,6 +214,7 @@
#define SHM_ALL "rmfixlnwaWtToOsAIcqF" /* all possible flags for 'shm' */ #define SHM_ALL "rmfixlnwaWtToOsAIcqF" /* all possible flags for 'shm' */
/* characters for p_go: */ /* characters for p_go: */
#define GO_TERMINAL '!' /* use terminal for system commands */
#define GO_ASEL 'a' /* autoselect */ #define GO_ASEL 'a' /* autoselect */
#define GO_ASELML 'A' /* autoselect modeless selection */ #define GO_ASELML 'A' /* autoselect modeless selection */
#define GO_BOT 'b' /* use bottom scrollbar */ #define GO_BOT 'b' /* use bottom scrollbar */
@@ -236,7 +237,7 @@
#define GO_FOOTER 'F' /* add footer */ #define GO_FOOTER 'F' /* add footer */
#define GO_VERTICAL 'v' /* arrange dialog buttons vertically */ #define GO_VERTICAL 'v' /* arrange dialog buttons vertically */
#define GO_KEEPWINSIZE 'k' /* keep GUI window size */ #define GO_KEEPWINSIZE 'k' /* keep GUI window size */
#define GO_ALL "aAbcefFghilmMprtTvk" /* all possible flags for 'go' */ #define GO_ALL "!aAbcefFghilmMprtTvk" /* all possible flags for 'go' */
/* flags for 'comments' option */ /* flags for 'comments' option */
#define COM_NEST 'n' /* comments strings nest */ #define COM_NEST 'n' /* comments strings nest */

View File

@@ -4154,10 +4154,13 @@ wait4pid(pid_t child, waitstatus *status)
return wait_pid; return wait_pid;
} }
#if defined(FEAT_JOB_CHANNEL) || !defined(USE_SYSTEM) || defined(PROTO) #if defined(FEAT_JOB_CHANNEL) \
|| !defined(USE_SYSTEM) \
|| (defined(FEAT_GUI) && defined(FEAT_TERMINAL)) \
|| defined(PROTO)
/* /*
* Parse "cmd" and put the white-separated parts in "argv". * Parse "cmd" and put the white-separated parts in "argv".
* "argv" is an allocated array with "argc" entries. * "argv" is an allocated array with "argc" entries and room for 4 more.
* Returns FAIL when out of memory. * Returns FAIL when out of memory.
*/ */
int int
@@ -4359,8 +4362,121 @@ may_send_sigint(int c UNUSED, pid_t pid UNUSED, pid_t wpid UNUSED)
# endif # endif
} }
int #if !defined(USE_SYSTEM) || (defined(FEAT_GUI) && defined(FEAT_TERMINAL))
mch_call_shell(
static int
build_argv(
char_u *cmd,
char ***argvp,
char_u **sh_tofree,
char_u **shcf_tofree)
{
char **argv = NULL;
int argc;
*sh_tofree = vim_strsave(p_sh);
if (*sh_tofree == NULL) /* out of memory */
return FAIL;
if (mch_parse_cmd(*sh_tofree, TRUE, &argv, &argc) == FAIL)
return FAIL;
*argvp = argv;
if (cmd != NULL)
{
char_u *s;
char_u *p;
if (extra_shell_arg != NULL)
argv[argc++] = (char *)extra_shell_arg;
/* Break 'shellcmdflag' into white separated parts. This doesn't
* handle quoted strings, they are very unlikely to appear. */
*shcf_tofree = alloc((unsigned)STRLEN(p_shcf) + 1);
if (*shcf_tofree == NULL) /* out of memory */
return FAIL;
s = *shcf_tofree;
p = p_shcf;
while (*p != NUL)
{
argv[argc++] = (char *)s;
while (*p && *p != ' ' && *p != TAB)
*s++ = *p++;
*s++ = NUL;
p = skipwhite(p);
}
argv[argc++] = (char *)cmd;
}
argv[argc] = NULL;
return OK;
}
#endif
#if defined(FEAT_GUI) && defined(FEAT_TERMINAL)
/*
* Use a terminal window to run a shell command in.
*/
static int
mch_call_shell_terminal(
char_u *cmd,
int options UNUSED) /* SHELL_*, see vim.h */
{
jobopt_T opt;
char **argv = NULL;
char_u *tofree1 = NULL;
char_u *tofree2 = NULL;
int retval = -1;
buf_T *buf;
aco_save_T aco;
oparg_T oa; /* operator arguments */
if (build_argv(cmd, &argv, &tofree1, &tofree2) == FAIL)
goto theend;
init_job_options(&opt);
ch_log(NULL, "starting terminal for system command '%s'", cmd);
buf = term_start(NULL, argv, &opt, TERM_START_SYSTEM);
/* Find a window to make "buf" curbuf. */
aucmd_prepbuf(&aco, buf);
clear_oparg(&oa);
while (term_use_loop())
{
if (oa.op_type == OP_NOP && oa.regname == NUL && !VIsual_active)
{
/* If terminal_loop() returns OK we got a key that is handled
* in Normal model. We don't do redrawing anyway. */
if (terminal_loop(TRUE) == OK)
normal_cmd(&oa, TRUE);
}
else
normal_cmd(&oa, TRUE);
}
retval = 0;
ch_log(NULL, "system command finished");
/* restore curwin/curbuf and a few other things */
aucmd_restbuf(&aco);
wait_return(TRUE);
do_buffer(DOBUF_WIPE, DOBUF_FIRST, FORWARD, buf->b_fnum, TRUE);
theend:
vim_free(argv);
vim_free(tofree1);
vim_free(tofree2);
return retval;
}
#endif
#ifdef USE_SYSTEM
/*
* Use system() to start the shell: simple but slow.
*/
static int
mch_call_shell_system(
char_u *cmd, char_u *cmd,
int options) /* SHELL_*, see vim.h */ int options) /* SHELL_*, see vim.h */
{ {
@@ -4369,7 +4485,6 @@ mch_call_shell(
char *ofn = NULL; char *ofn = NULL;
#endif #endif
int tmode = cur_tmode; int tmode = cur_tmode;
#ifdef USE_SYSTEM /* use system() to start the shell: simple but slow */
char_u *newcmd; /* only needed for unix */ char_u *newcmd; /* only needed for unix */
int x; int x;
@@ -4443,14 +4558,23 @@ mch_call_shell(
restore_clipboard(); restore_clipboard();
# endif # endif
return x; return x;
}
#else /* USE_SYSTEM */ /* don't use system(), use fork()/exec() */ #else /* USE_SYSTEM */
# define EXEC_FAILED 122 /* Exit code when shell didn't execute. Don't use # define EXEC_FAILED 122 /* Exit code when shell didn't execute. Don't use
127, some shells use that already */ 127, some shells use that already */
# define OPEN_NULL_FAILED 123 /* Exit code if /dev/null can't be opened */ # define OPEN_NULL_FAILED 123 /* Exit code if /dev/null can't be opened */
char_u *newcmd; /*
* Don't use system(), use fork()/exec().
*/
static int
mch_call_shell_fork(
char_u *cmd,
int options) /* SHELL_*, see vim.h */
{
int tmode = cur_tmode;
pid_t pid; pid_t pid;
pid_t wpid = 0; pid_t wpid = 0;
pid_t wait_pid = 0; pid_t wait_pid = 0;
@@ -4461,8 +4585,8 @@ mch_call_shell(
# endif # endif
int retval = -1; int retval = -1;
char **argv = NULL; char **argv = NULL;
int argc; char_u *tofree1 = NULL;
char_u *p_shcf_copy = NULL; char_u *tofree2 = NULL;
int i; int i;
char_u *p; char_u *p;
int pty_master_fd = -1; /* for pty's */ int pty_master_fd = -1; /* for pty's */
@@ -4474,44 +4598,13 @@ mch_call_shell(
int pipe_error = FALSE; int pipe_error = FALSE;
int did_settmode = FALSE; /* settmode(TMODE_RAW) called */ int did_settmode = FALSE; /* settmode(TMODE_RAW) called */
newcmd = vim_strsave(p_sh);
if (newcmd == NULL) /* out of memory */
goto error;
out_flush(); out_flush();
if (options & SHELL_COOKED) if (options & SHELL_COOKED)
settmode(TMODE_COOK); /* set to normal mode */ settmode(TMODE_COOK); /* set to normal mode */
if (mch_parse_cmd(newcmd, TRUE, &argv, &argc) == FAIL) if (build_argv(cmd, &argv, &tofree1, &tofree2) == FAIL)
goto error; goto error;
if (cmd != NULL)
{
char_u *s;
if (extra_shell_arg != NULL)
argv[argc++] = (char *)extra_shell_arg;
/* Break 'shellcmdflag' into white separated parts. This doesn't
* handle quoted strings, they are very unlikely to appear. */
p_shcf_copy = alloc((unsigned)STRLEN(p_shcf) + 1);
if (p_shcf_copy == NULL) /* out of memory */
goto error;
s = p_shcf_copy;
p = p_shcf;
while (*p != NUL)
{
argv[argc++] = (char *)s;
while (*p && *p != ' ' && *p != TAB)
*s++ = *p++;
*s++ = NUL;
p = skipwhite(p);
}
argv[argc++] = (char *)cmd;
}
argv[argc] = NULL;
/* /*
* For the GUI, when writing the output into the buffer and when reading * For the GUI, when writing the output into the buffer and when reading
* input from the buffer: Try using a pseudo-tty to get the stdin/stdout * input from the buffer: Try using a pseudo-tty to get the stdin/stdout
@@ -5319,8 +5412,6 @@ finished:
MSG_PUTS(_("\nCommand terminated\n")); MSG_PUTS(_("\nCommand terminated\n"));
} }
} }
vim_free(argv);
vim_free(p_shcf_copy);
error: error:
if (!did_settmode) if (!did_settmode)
@@ -5329,11 +5420,28 @@ error:
# ifdef FEAT_TITLE # ifdef FEAT_TITLE
resettitle(); resettitle();
# endif # endif
vim_free(newcmd); vim_free(argv);
vim_free(tofree1);
vim_free(tofree2);
return retval; return retval;
}
#endif /* USE_SYSTEM */ #endif /* USE_SYSTEM */
int
mch_call_shell(
char_u *cmd,
int options) /* SHELL_*, see vim.h */
{
#if defined(FEAT_GUI) && defined(FEAT_TERMINAL)
if (gui.in_use && vim_strchr(p_go, GO_TERMINAL) != NULL)
return mch_call_shell_terminal(cmd, options);
#endif
#ifdef USE_SYSTEM
return mch_call_shell_system(cmd, options);
#else
return mch_call_shell_fork(cmd, options);
#endif
} }
#if defined(FEAT_JOB_CHANNEL) || defined(PROTO) #if defined(FEAT_JOB_CHANNEL) || defined(PROTO)

View File

@@ -66,7 +66,7 @@ void job_set_options(job_T *job, jobopt_T *opt);
void job_stop_on_exit(void); void job_stop_on_exit(void);
int has_pending_job(void); int has_pending_job(void);
void job_check_ended(void); void job_check_ended(void);
job_T *job_start(typval_T *argvars, jobopt_T *opt_arg); job_T *job_start(typval_T *argvars, char **argv_arg, jobopt_T *opt_arg);
char *job_status(job_T *job); char *job_status(job_T *job);
void job_info(job_T *job, dict_T *dict); void job_info(job_T *job, dict_T *dict);
int job_stop(job_T *job, typval_T *argvars, char *type); int job_stop(job_T *job, typval_T *argvars, char *type);

View File

@@ -1,4 +1,6 @@
/* terminal.c */ /* terminal.c */
void init_job_options(jobopt_T *opt);
buf_T *term_start(typval_T *argvar, char **argv, jobopt_T *opt, int flags);
void ex_terminal(exarg_T *eap); void ex_terminal(exarg_T *eap);
int term_write_session(FILE *fd, win_T *wp); int term_write_session(FILE *fd, win_T *wp);
int term_should_restore(buf_T *buf); int term_should_restore(buf_T *buf);

View File

@@ -38,10 +38,14 @@
* in tl_scrollback are no longer used. * in tl_scrollback are no longer used.
* *
* TODO: * TODO:
* - When using 'termguicolors' still use the 16 ANSI colors as-is. Helps for * - Make terminal close by default when started without a command. Add
* - In the GUI use a terminal emulator for :!cmd. Make the height the same as * ++noclose argument.
* the window and position it higher up when it gets filled, so it looks like * - Win32: In the GUI use a terminal emulator for :!cmd.
* the text scrolls up. * - 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.
* - implement term_setsize() * - implement term_setsize()
* - Copy text in the vterm to the Vim buffer once in a while, so that * - Copy text in the vterm to the Vim buffer once in a while, so that
* completion works. * completion works.
@@ -104,6 +108,10 @@ struct terminal_S {
VTerm *tl_vterm; VTerm *tl_vterm;
job_T *tl_job; job_T *tl_job;
buf_T *tl_buffer; buf_T *tl_buffer;
#if defined(FEAT_GUI)
int tl_system; /* when non-zero used for :!cmd output */
int tl_toprow; /* row with first line of system terminal */
#endif
/* Set when setting the size of a vterm, reset after redrawing. */ /* Set when setting the size of a vterm, reset after redrawing. */
int tl_vterm_size_changed; int tl_vterm_size_changed;
@@ -175,10 +183,13 @@ static term_T *in_terminal_loop = NULL;
/* /*
* Functions with separate implementation for MS-Windows and Unix-like systems. * Functions with separate implementation for MS-Windows and Unix-like systems.
*/ */
static int term_and_job_init(term_T *term, typval_T *argvar, jobopt_T *opt); static int term_and_job_init(term_T *term, typval_T *argvar, char **argv, jobopt_T *opt);
static int create_pty_only(term_T *term, jobopt_T *opt); static int create_pty_only(term_T *term, jobopt_T *opt);
static void term_report_winsize(term_T *term, int rows, int cols); static void term_report_winsize(term_T *term, int rows, int cols);
static void term_free_vterm(term_T *term); static void term_free_vterm(term_T *term);
#ifdef FEAT_GUI
static void update_system_term(term_T *term);
#endif
/* The character that we know (or assume) that the terminal expects for the /* The character that we know (or assume) that the terminal expects for the
* backspace key. */ * backspace key. */
@@ -209,6 +220,16 @@ static int desired_cursor_blink = -1;
static void static void
set_term_and_win_size(term_T *term) set_term_and_win_size(term_T *term)
{ {
#ifdef FEAT_GUI
if (term->tl_system)
{
/* Use the whole screen for the system command. However, it will start
* at the command line and scroll up as needed, using tl_toprow. */
term->tl_rows = Rows;
term->tl_cols = Columns;
}
else
#endif
if (*curwin->w_p_tms != NUL) if (*curwin->w_p_tms != NUL)
{ {
char_u *p = vim_strchr(curwin->w_p_tms, 'x') + 1; char_u *p = vim_strchr(curwin->w_p_tms, 'x') + 1;
@@ -236,7 +257,7 @@ set_term_and_win_size(term_T *term)
* Initialize job options for a terminal job. * Initialize job options for a terminal job.
* Caller may overrule some of them. * Caller may overrule some of them.
*/ */
static void void
init_job_options(jobopt_T *opt) init_job_options(jobopt_T *opt)
{ {
clear_job_options(opt); clear_job_options(opt);
@@ -301,12 +322,17 @@ term_close_buffer(buf_T *buf, buf_T *old_curbuf)
/* /*
* Start a terminal window and return its buffer. * Start a terminal window and return its buffer.
* When "without_job" is TRUE only create the buffer, b_term and open the * Use either "argvar" or "argv", the other must be NULL.
* window. * When "flags" has TERM_START_NOJOB only create the buffer, b_term and open
* the window.
* Returns NULL when failed. * Returns NULL when failed.
*/ */
static buf_T * buf_T *
term_start(typval_T *argvar, jobopt_T *opt, int without_job, int forceit) term_start(
typval_T *argvar,
char **argv,
jobopt_T *opt,
int flags)
{ {
exarg_T split_ea; exarg_T split_ea;
win_T *old_curwin = curwin; win_T *old_curwin = curwin;
@@ -334,26 +360,31 @@ term_start(typval_T *argvar, jobopt_T *opt, int without_job, int forceit)
term->tl_cursor_visible = TRUE; term->tl_cursor_visible = TRUE;
term->tl_cursor_shape = VTERM_PROP_CURSORSHAPE_BLOCK; term->tl_cursor_shape = VTERM_PROP_CURSORSHAPE_BLOCK;
term->tl_finish = opt->jo_term_finish; term->tl_finish = opt->jo_term_finish;
#ifdef FEAT_GUI
term->tl_system = (flags & TERM_START_SYSTEM);
#endif
ga_init2(&term->tl_scrollback, sizeof(sb_line_T), 300); ga_init2(&term->tl_scrollback, sizeof(sb_line_T), 300);
vim_memset(&split_ea, 0, sizeof(split_ea)); vim_memset(&split_ea, 0, sizeof(split_ea));
if (opt->jo_curwin) if (opt->jo_curwin)
{ {
/* Create a new buffer in the current window. */ /* Create a new buffer in the current window. */
if (!can_abandon(curbuf, forceit)) if (!can_abandon(curbuf, flags & TERM_START_FORCEIT))
{ {
no_write_message(); no_write_message();
vim_free(term); vim_free(term);
return NULL; return NULL;
} }
if (do_ecmd(0, NULL, NULL, &split_ea, ECMD_ONE, if (do_ecmd(0, NULL, NULL, &split_ea, ECMD_ONE,
ECMD_HIDE + (forceit ? ECMD_FORCEIT : 0), curwin) == FAIL) ECMD_HIDE
+ ((flags & TERM_START_FORCEIT) ? ECMD_FORCEIT : 0),
curwin) == FAIL)
{ {
vim_free(term); vim_free(term);
return NULL; return NULL;
} }
} }
else if (opt->jo_hidden) else if (opt->jo_hidden || (flags & TERM_START_SYSTEM))
{ {
buf_T *buf; buf_T *buf;
@@ -418,6 +449,8 @@ term_start(typval_T *argvar, jobopt_T *opt, int without_job, int forceit)
if (opt->jo_term_name != NULL) if (opt->jo_term_name != NULL)
curbuf->b_ffname = vim_strsave(opt->jo_term_name); curbuf->b_ffname = vim_strsave(opt->jo_term_name);
else if (argv != NULL)
curbuf->b_ffname = vim_strsave((char_u *)"!system");
else else
{ {
int i; int i;
@@ -476,12 +509,12 @@ term_start(typval_T *argvar, jobopt_T *opt, int without_job, int forceit)
set_term_and_win_size(term); set_term_and_win_size(term);
setup_job_options(opt, term->tl_rows, term->tl_cols); setup_job_options(opt, term->tl_rows, term->tl_cols);
if (without_job) if (flags & TERM_START_NOJOB)
return curbuf; return curbuf;
#if defined(FEAT_SESSION) #if defined(FEAT_SESSION)
/* Remember the command for the session file. */ /* Remember the command for the session file. */
if (opt->jo_term_norestore) if (opt->jo_term_norestore || argv != NULL)
{ {
term->tl_command = vim_strsave((char_u *)"NONE"); term->tl_command = vim_strsave((char_u *)"NONE");
} }
@@ -533,12 +566,13 @@ term_start(typval_T *argvar, jobopt_T *opt, int without_job, int forceit)
} }
/* System dependent: setup the vterm and maybe start the job in it. */ /* System dependent: setup the vterm and maybe start the job in it. */
if (argvar->v_type == VAR_STRING if (argv == NULL
&& argvar->v_type == VAR_STRING
&& argvar->vval.v_string != NULL && argvar->vval.v_string != NULL
&& STRCMP(argvar->vval.v_string, "NONE") == 0) && STRCMP(argvar->vval.v_string, "NONE") == 0)
res = create_pty_only(term, opt); res = create_pty_only(term, opt);
else else
res = term_and_job_init(term, argvar, opt); res = term_and_job_init(term, argvar, argv, opt);
newbuf = curbuf; newbuf = curbuf;
if (res == OK) if (res == OK)
@@ -546,19 +580,26 @@ term_start(typval_T *argvar, jobopt_T *opt, int without_job, int forceit)
/* Get and remember the size we ended up with. Update the pty. */ /* Get and remember the size we ended up with. Update the pty. */
vterm_get_size(term->tl_vterm, &term->tl_rows, &term->tl_cols); vterm_get_size(term->tl_vterm, &term->tl_rows, &term->tl_cols);
term_report_winsize(term, term->tl_rows, term->tl_cols); term_report_winsize(term, term->tl_rows, term->tl_cols);
#ifdef FEAT_GUI
if (term->tl_system)
{
/* display first line below typed command */
term->tl_toprow = msg_row + 1;
term->tl_dirty_row_end = 0;
}
#endif
/* Make sure we don't get stuck on sending keys to the job, it leads to /* Make sure we don't get stuck on sending keys to the job, it leads to
* a deadlock if the job is waiting for Vim to read. */ * a deadlock if the job is waiting for Vim to read. */
channel_set_nonblock(term->tl_job->jv_channel, PART_IN); channel_set_nonblock(term->tl_job->jv_channel, PART_IN);
if (!opt->jo_hidden) if (old_curbuf == NULL)
{ {
++curbuf->b_locked; ++curbuf->b_locked;
apply_autocmds(EVENT_BUFWINENTER, NULL, NULL, FALSE, curbuf); apply_autocmds(EVENT_BUFWINENTER, NULL, NULL, FALSE, curbuf);
--curbuf->b_locked; --curbuf->b_locked;
} }
else
if (old_curbuf != NULL)
{ {
--curbuf->b_nwindows; --curbuf->b_nwindows;
curbuf = old_curbuf; curbuf = old_curbuf;
@@ -572,7 +613,7 @@ term_start(typval_T *argvar, jobopt_T *opt, int without_job, int forceit)
return NULL; return NULL;
} }
apply_autocmds(EVENT_TERMINALOPEN, NULL, NULL, FALSE, curbuf); apply_autocmds(EVENT_TERMINALOPEN, NULL, NULL, FALSE, newbuf);
return newbuf; return newbuf;
} }
@@ -671,7 +712,7 @@ ex_terminal(exarg_T *eap)
argvar[0].v_type = VAR_STRING; argvar[0].v_type = VAR_STRING;
argvar[0].vval.v_string = cmd; argvar[0].vval.v_string = cmd;
argvar[1].v_type = VAR_UNKNOWN; argvar[1].v_type = VAR_UNKNOWN;
term_start(argvar, &opt, FALSE, eap->forceit); term_start(argvar, NULL, &opt, eap->forceit ? TERM_START_FORCEIT : 0);
vim_free(tofree); vim_free(tofree);
theend: theend:
@@ -833,7 +874,13 @@ update_cursor(term_T *term, int redraw)
{ {
if (term->tl_normal_mode) if (term->tl_normal_mode)
return; return;
setcursor(); #ifdef FEAT_GUI
if (term->tl_system)
windgoto(term->tl_cursor_pos.row + term->tl_toprow,
term->tl_cursor_pos.col);
else
#endif
setcursor();
if (redraw) if (redraw)
{ {
if (term->tl_buffer == curbuf && term->tl_cursor_visible) if (term->tl_buffer == curbuf && term->tl_cursor_visible)
@@ -867,6 +914,15 @@ write_to_term(buf_T *buffer, char_u *msg, channel_T *channel)
ch_log(channel, "writing %d bytes to terminal", (int)len); ch_log(channel, "writing %d bytes to terminal", (int)len);
term_write_job_output(term, msg, len); term_write_job_output(term, msg, len);
#ifdef FEAT_GUI
if (term->tl_system)
{
/* show system output, scrolling up the screen as needed */
update_system_term(term);
update_cursor(term, TRUE);
}
else
#endif
/* In Terminal-Normal mode we are displaying the buffer, not the terminal /* In Terminal-Normal mode we are displaying the buffer, not the terminal
* contents, thus no screen update is needed. */ * contents, thus no screen update is needed. */
if (!term->tl_normal_mode) if (!term->tl_normal_mode)
@@ -1905,11 +1961,15 @@ terminal_loop(int blocking)
while (blocking || vpeekc_nomap() != NUL) while (blocking || vpeekc_nomap() != NUL)
{ {
/* TODO: skip screen update when handling a sequence of keys. */ #ifdef FEAT_GUI
/* Repeat redrawing in case a message is received while redrawing. */ if (!curbuf->b_term->tl_system)
while (must_redraw != 0) #endif
if (update_screen(0) == FAIL) /* TODO: skip screen update when handling a sequence of keys. */
break; /* Repeat redrawing in case a message is received while redrawing.
*/
while (must_redraw != 0)
if (update_screen(0) == FAIL)
break;
update_cursor(curbuf->b_term, FALSE); update_cursor(curbuf->b_term, FALSE);
restore_cursor = TRUE; restore_cursor = TRUE;
@@ -2585,6 +2645,139 @@ term_channel_closed(channel_T *ch)
} }
} }
/*
* Fill one screen line from a line of the terminal.
* Advances "pos" to past the last column.
*/
static void
term_line2screenline(VTermScreen *screen, VTermPos *pos, int max_col)
{
int off = screen_get_current_line_off();
for (pos->col = 0; pos->col < max_col; )
{
VTermScreenCell cell;
int c;
if (vterm_screen_get_cell(screen, *pos, &cell) == 0)
vim_memset(&cell, 0, sizeof(cell));
c = cell.chars[0];
if (c == NUL)
{
ScreenLines[off] = ' ';
if (enc_utf8)
ScreenLinesUC[off] = NUL;
}
else
{
if (enc_utf8)
{
int i;
/* composing chars */
for (i = 0; i < Screen_mco
&& i + 1 < VTERM_MAX_CHARS_PER_CELL; ++i)
{
ScreenLinesC[i][off] = cell.chars[i + 1];
if (cell.chars[i + 1] == 0)
break;
}
if (c >= 0x80 || (Screen_mco > 0
&& ScreenLinesC[0][off] != 0))
{
ScreenLines[off] = ' ';
ScreenLinesUC[off] = c;
}
else
{
ScreenLines[off] = c;
ScreenLinesUC[off] = NUL;
}
}
#ifdef WIN3264
else if (has_mbyte && c >= 0x80)
{
char_u mb[MB_MAXBYTES+1];
WCHAR wc = c;
if (WideCharToMultiByte(GetACP(), 0, &wc, 1,
(char*)mb, 2, 0, 0) > 1)
{
ScreenLines[off] = mb[0];
ScreenLines[off + 1] = mb[1];
cell.width = mb_ptr2cells(mb);
}
else
ScreenLines[off] = c;
}
#endif
else
ScreenLines[off] = c;
}
ScreenAttrs[off] = cell2attr(cell.attrs, cell.fg, cell.bg);
++pos->col;
++off;
if (cell.width == 2)
{
if (enc_utf8)
ScreenLinesUC[off] = NUL;
/* don't set the second byte to NUL for a DBCS encoding, it
* has been set above */
if (enc_utf8 || !has_mbyte)
ScreenLines[off] = NUL;
++pos->col;
++off;
}
}
}
static void
update_system_term(term_T *term)
{
VTermPos pos;
VTermScreen *screen;
if (term->tl_vterm == NULL)
return;
screen = vterm_obtain_screen(term->tl_vterm);
/* Scroll up to make more room for terminal lines if needed. */
while (term->tl_toprow > 0
&& (Rows - term->tl_toprow) < term->tl_dirty_row_end)
{
int save_p_more = p_more;
p_more = FALSE;
msg_row = Rows - 1;
msg_puts((char_u *)"\n");
p_more = save_p_more;
--term->tl_toprow;
}
for (pos.row = term->tl_dirty_row_start; pos.row < term->tl_dirty_row_end
&& pos.row < Rows; ++pos.row)
{
if (pos.row < term->tl_rows)
{
int max_col = MIN(Columns, term->tl_cols);
term_line2screenline(screen, &pos, max_col);
}
else
pos.col = 0;
screen_line(term->tl_toprow + pos.row, 0, pos.col, Columns, FALSE);
}
term->tl_dirty_row_start = MAX_ROW;
term->tl_dirty_row_end = 0;
update_cursor(term, TRUE);
}
/* /*
* Called to update a window that contains an active terminal. * Called to update a window that contains an active terminal.
* Returns FAIL when there is no terminal running in this window or in * Returns FAIL when there is no terminal running in this window or in
@@ -2650,90 +2843,11 @@ term_update_window(win_T *wp)
for (pos.row = term->tl_dirty_row_start; pos.row < term->tl_dirty_row_end for (pos.row = term->tl_dirty_row_start; pos.row < term->tl_dirty_row_end
&& pos.row < wp->w_height; ++pos.row) && pos.row < wp->w_height; ++pos.row)
{ {
int off = screen_get_current_line_off();
int max_col = MIN(wp->w_width, term->tl_cols);
if (pos.row < term->tl_rows) if (pos.row < term->tl_rows)
{ {
for (pos.col = 0; pos.col < max_col; ) int max_col = MIN(wp->w_width, term->tl_cols);
{
VTermScreenCell cell;
int c;
if (vterm_screen_get_cell(screen, pos, &cell) == 0) term_line2screenline(screen, &pos, max_col);
vim_memset(&cell, 0, sizeof(cell));
c = cell.chars[0];
if (c == NUL)
{
ScreenLines[off] = ' ';
if (enc_utf8)
ScreenLinesUC[off] = NUL;
}
else
{
if (enc_utf8)
{
int i;
/* composing chars */
for (i = 0; i < Screen_mco
&& i + 1 < VTERM_MAX_CHARS_PER_CELL; ++i)
{
ScreenLinesC[i][off] = cell.chars[i + 1];
if (cell.chars[i + 1] == 0)
break;
}
if (c >= 0x80 || (Screen_mco > 0
&& ScreenLinesC[0][off] != 0))
{
ScreenLines[off] = ' ';
ScreenLinesUC[off] = c;
}
else
{
ScreenLines[off] = c;
ScreenLinesUC[off] = NUL;
}
}
#ifdef WIN3264
else if (has_mbyte && c >= 0x80)
{
char_u mb[MB_MAXBYTES+1];
WCHAR wc = c;
if (WideCharToMultiByte(GetACP(), 0, &wc, 1,
(char*)mb, 2, 0, 0) > 1)
{
ScreenLines[off] = mb[0];
ScreenLines[off + 1] = mb[1];
cell.width = mb_ptr2cells(mb);
}
else
ScreenLines[off] = c;
}
#endif
else
ScreenLines[off] = c;
}
ScreenAttrs[off] = cell2attr(cell.attrs, cell.fg, cell.bg);
++pos.col;
++off;
if (cell.width == 2)
{
if (enc_utf8)
ScreenLinesUC[off] = NUL;
/* don't set the second byte to NUL for a DBCS encoding, it
* has been set above */
if (enc_utf8 || !has_mbyte)
ScreenLines[off] = NUL;
++pos.col;
++off;
}
}
} }
else else
pos.col = 0; pos.col = 0;
@@ -3623,7 +3737,7 @@ term_load_dump(typval_T *argvars, typval_T *rettv, int do_diff)
/* TODO: use the file name arguments for the buffer name */ /* TODO: use the file name arguments for the buffer name */
opt.jo_term_name = (char_u *)"dump diff"; opt.jo_term_name = (char_u *)"dump diff";
buf = term_start(&argvars[0], &opt, TRUE, FALSE); buf = term_start(&argvars[0], NULL, &opt, TERM_START_NOJOB);
if (buf != NULL && buf->b_term != NULL) if (buf != NULL && buf->b_term != NULL)
{ {
int i; int i;
@@ -4396,7 +4510,7 @@ f_term_start(typval_T *argvars, typval_T *rettv)
if (opt.jo_vertical) if (opt.jo_vertical)
cmdmod.split = WSP_VERT; cmdmod.split = WSP_VERT;
buf = term_start(&argvars[0], &opt, FALSE, FALSE); buf = term_start(&argvars[0], NULL, &opt, 0);
if (buf != NULL && buf->b_term != NULL) if (buf != NULL && buf->b_term != NULL)
rettv->vval.v_number = buf->b_fnum; rettv->vval.v_number = buf->b_fnum;
@@ -4592,6 +4706,7 @@ dyn_winpty_init(int verbose)
term_and_job_init( term_and_job_init(
term_T *term, term_T *term,
typval_T *argvar, typval_T *argvar,
char **argv UNUSED,
jobopt_T *opt) jobopt_T *opt)
{ {
WCHAR *cmd_wchar = NULL; WCHAR *cmd_wchar = NULL;
@@ -4880,18 +4995,20 @@ terminal_enabled(void)
* Create a new terminal of "rows" by "cols" cells. * Create a new terminal of "rows" by "cols" cells.
* Start job for "cmd". * Start job for "cmd".
* Store the pointers in "term". * Store the pointers in "term".
* When "argv" is not NULL then "argvar" is not used.
* Return OK or FAIL. * Return OK or FAIL.
*/ */
static int static int
term_and_job_init( term_and_job_init(
term_T *term, term_T *term,
typval_T *argvar, typval_T *argvar,
char **argv,
jobopt_T *opt) jobopt_T *opt)
{ {
create_vterm(term, term->tl_rows, term->tl_cols); create_vterm(term, term->tl_rows, term->tl_cols);
/* This will change a string in "argvar". */ /* This may change a string in "argvar". */
term->tl_job = job_start(argvar, opt); term->tl_job = job_start(argvar, argv, opt);
if (term->tl_job != NULL) if (term->tl_job != NULL)
++term->tl_job->jv_refcount; ++term->tl_job->jv_refcount;

View File

@@ -766,6 +766,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
1609,
/**/ /**/
1608, 1608,
/**/ /**/

View File

@@ -2543,4 +2543,9 @@ typedef enum {
#define REPLACE_CR_NCHAR -1 #define REPLACE_CR_NCHAR -1
#define REPLACE_NL_NCHAR -2 #define REPLACE_NL_NCHAR -2
/* flags for term_start() */
#define TERM_START_NOJOB 1
#define TERM_START_FORCEIT 2
#define TERM_START_SYSTEM 4
#endif /* VIM__H */ #endif /* VIM__H */