# HG changeset patch # User Christian Brabandt # Date 1521230408 -3600 # Node ID 6faef782f50b5ec0184ab33144c4d55340922ee0 # Parent e9121e612cbc8cd2e7b5cd960749f08db9ddcdef patch 8.0.1609: shell commands in the GUI use a dumb terminal commit https://github.com/vim/vim/commit/135682517bc378cfdb63fe3a6e3553935f69f6ce Author: Bram Moolenaar Date: Fri Mar 16 20:46:58 2018 +0100 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. diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -3845,7 +3845,14 @@ A jump table for the options with a shor To avoid problems with flags that are added in the future, use the "+=" 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'* 'a' Autoselect: If present, then whenever VISUAL mode is started, or the Visual area extended, Vim tries to become the owner of diff --git a/src/channel.c b/src/channel.c --- a/src/channel.c +++ b/src/channel.c @@ -5383,11 +5383,13 @@ job_check_ended(void) /* * 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. * Returns NULL when out of memory. */ 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; char_u *cmd = NULL; @@ -5474,6 +5476,13 @@ job_start(typval_T *argvars, jobopt_T *o job_set_options(job, &opt); +#ifdef USE_ARGV + if (argv_arg != NULL) + { + argv = argv_arg; + } + else +#endif if (argvars[0].v_type == VAR_STRING) { /* Command is a string. */ @@ -5551,7 +5560,8 @@ job_start(typval_T *argvars, jobopt_T *o theend: #ifdef USE_ARGV - vim_free(argv); + if (argv != argv_arg) + vim_free(argv); #else vim_free(ga.ga_data); #endif diff --git a/src/evalfunc.c b/src/evalfunc.c --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -7032,7 +7032,7 @@ f_job_start(typval_T *argvars, typval_T rettv->v_type = VAR_JOB; if (check_restricted() || check_secure()) return; - rettv->vval.v_job = job_start(argvars, NULL); + rettv->vval.v_job = job_start(argvars, NULL, NULL); } /* diff --git a/src/option.h b/src/option.h --- a/src/option.h +++ b/src/option.h @@ -214,6 +214,7 @@ #define SHM_ALL "rmfixlnwaWtToOsAIcqF" /* all possible flags for 'shm' */ /* characters for p_go: */ +#define GO_TERMINAL '!' /* use terminal for system commands */ #define GO_ASEL 'a' /* autoselect */ #define GO_ASELML 'A' /* autoselect modeless selection */ #define GO_BOT 'b' /* use bottom scrollbar */ @@ -236,7 +237,7 @@ #define GO_FOOTER 'F' /* add footer */ #define GO_VERTICAL 'v' /* arrange dialog buttons vertically */ #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 */ #define COM_NEST 'n' /* comments strings nest */ diff --git a/src/os_unix.c b/src/os_unix.c --- a/src/os_unix.c +++ b/src/os_unix.c @@ -4154,10 +4154,13 @@ wait4pid(pid_t child, waitstatus *status 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". - * "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. */ int @@ -4359,8 +4362,121 @@ may_send_sigint(int c UNUSED, pid_t pid # endif } - int -mch_call_shell( +#if !defined(USE_SYSTEM) || (defined(FEAT_GUI) && defined(FEAT_TERMINAL)) + + 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, int options) /* SHELL_*, see vim.h */ { @@ -4369,7 +4485,6 @@ mch_call_shell( char *ofn = NULL; #endif int tmode = cur_tmode; -#ifdef USE_SYSTEM /* use system() to start the shell: simple but slow */ char_u *newcmd; /* only needed for unix */ int x; @@ -4443,14 +4558,23 @@ mch_call_shell( restore_clipboard(); # endif 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 127, some shells use that already */ # 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 wpid = 0; pid_t wait_pid = 0; @@ -4461,8 +4585,8 @@ mch_call_shell( # endif int retval = -1; char **argv = NULL; - int argc; - char_u *p_shcf_copy = NULL; + char_u *tofree1 = NULL; + char_u *tofree2 = NULL; int i; char_u *p; int pty_master_fd = -1; /* for pty's */ @@ -4474,44 +4598,13 @@ mch_call_shell( int pipe_error = FALSE; int did_settmode = FALSE; /* settmode(TMODE_RAW) called */ - newcmd = vim_strsave(p_sh); - if (newcmd == NULL) /* out of memory */ - goto error; - out_flush(); if (options & SHELL_COOKED) 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; - 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 * input from the buffer: Try using a pseudo-tty to get the stdin/stdout @@ -5319,8 +5412,6 @@ finished: MSG_PUTS(_("\nCommand terminated\n")); } } - vim_free(argv); - vim_free(p_shcf_copy); error: if (!did_settmode) @@ -5329,11 +5420,28 @@ error: # ifdef FEAT_TITLE resettitle(); # endif - vim_free(newcmd); + vim_free(argv); + vim_free(tofree1); + vim_free(tofree2); return retval; - +} #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) diff --git a/src/proto/channel.pro b/src/proto/channel.pro --- a/src/proto/channel.pro +++ b/src/proto/channel.pro @@ -66,7 +66,7 @@ void job_set_options(job_T *job, jobopt_ void job_stop_on_exit(void); int has_pending_job(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); void job_info(job_T *job, dict_T *dict); int job_stop(job_T *job, typval_T *argvars, char *type); diff --git a/src/proto/terminal.pro b/src/proto/terminal.pro --- a/src/proto/terminal.pro +++ b/src/proto/terminal.pro @@ -1,4 +1,6 @@ /* 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); int term_write_session(FILE *fd, win_T *wp); int term_should_restore(buf_T *buf); diff --git a/src/terminal.c b/src/terminal.c --- a/src/terminal.c +++ b/src/terminal.c @@ -38,10 +38,14 @@ * in tl_scrollback are no longer used. * * TODO: - * - When using 'termguicolors' still use the 16 ANSI colors as-is. Helps for - * - In the GUI use a terminal emulator for :!cmd. Make the height the same as - * the window and position it higher up when it gets filled, so it looks like - * the text scrolls up. + * - Make terminal close by default when started without a command. Add + * ++noclose argument. + * - Win32: In the GUI use a terminal emulator for :!cmd. + * - 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() * - Copy text in the vterm to the Vim buffer once in a while, so that * completion works. @@ -104,6 +108,10 @@ struct terminal_S { VTerm *tl_vterm; job_T *tl_job; 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. */ 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. */ -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 void term_report_winsize(term_T *term, int rows, int cols); 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 * backspace key. */ @@ -209,6 +220,16 @@ static int desired_cursor_blink = -1; static void 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) { 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. * Caller may overrule some of them. */ - static void + void init_job_options(jobopt_T *opt) { clear_job_options(opt); @@ -301,12 +322,17 @@ term_close_buffer(buf_T *buf, buf_T *old /* * Start a terminal window and return its buffer. - * When "without_job" is TRUE only create the buffer, b_term and open the - * window. + * Use either "argvar" or "argv", the other must be NULL. + * When "flags" has TERM_START_NOJOB only create the buffer, b_term and open + * the window. * Returns NULL when failed. */ - static buf_T * -term_start(typval_T *argvar, jobopt_T *opt, int without_job, int forceit) + buf_T * +term_start( + typval_T *argvar, + char **argv, + jobopt_T *opt, + int flags) { exarg_T split_ea; win_T *old_curwin = curwin; @@ -334,26 +360,31 @@ term_start(typval_T *argvar, jobopt_T *o term->tl_cursor_visible = TRUE; term->tl_cursor_shape = VTERM_PROP_CURSORSHAPE_BLOCK; 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); vim_memset(&split_ea, 0, sizeof(split_ea)); if (opt->jo_curwin) { /* Create a new buffer in the current window. */ - if (!can_abandon(curbuf, forceit)) + if (!can_abandon(curbuf, flags & TERM_START_FORCEIT)) { no_write_message(); vim_free(term); return NULL; } 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); return NULL; } } - else if (opt->jo_hidden) + else if (opt->jo_hidden || (flags & TERM_START_SYSTEM)) { buf_T *buf; @@ -418,6 +449,8 @@ term_start(typval_T *argvar, jobopt_T *o if (opt->jo_term_name != NULL) curbuf->b_ffname = vim_strsave(opt->jo_term_name); + else if (argv != NULL) + curbuf->b_ffname = vim_strsave((char_u *)"!system"); else { int i; @@ -476,12 +509,12 @@ term_start(typval_T *argvar, jobopt_T *o set_term_and_win_size(term); setup_job_options(opt, term->tl_rows, term->tl_cols); - if (without_job) + if (flags & TERM_START_NOJOB) return curbuf; #if defined(FEAT_SESSION) /* 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"); } @@ -533,12 +566,13 @@ term_start(typval_T *argvar, jobopt_T *o } /* 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 && STRCMP(argvar->vval.v_string, "NONE") == 0) res = create_pty_only(term, opt); else - res = term_and_job_init(term, argvar, opt); + res = term_and_job_init(term, argvar, argv, opt); newbuf = curbuf; if (res == OK) @@ -546,19 +580,26 @@ term_start(typval_T *argvar, jobopt_T *o /* Get and remember the size we ended up with. Update the pty. */ vterm_get_size(term->tl_vterm, &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 * a deadlock if the job is waiting for Vim to read. */ channel_set_nonblock(term->tl_job->jv_channel, PART_IN); - if (!opt->jo_hidden) + if (old_curbuf == NULL) { ++curbuf->b_locked; apply_autocmds(EVENT_BUFWINENTER, NULL, NULL, FALSE, curbuf); --curbuf->b_locked; } - - if (old_curbuf != NULL) + else { --curbuf->b_nwindows; curbuf = old_curbuf; @@ -572,7 +613,7 @@ term_start(typval_T *argvar, jobopt_T *o return NULL; } - apply_autocmds(EVENT_TERMINALOPEN, NULL, NULL, FALSE, curbuf); + apply_autocmds(EVENT_TERMINALOPEN, NULL, NULL, FALSE, newbuf); return newbuf; } @@ -671,7 +712,7 @@ ex_terminal(exarg_T *eap) argvar[0].v_type = VAR_STRING; argvar[0].vval.v_string = cmd; 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); theend: @@ -833,7 +874,13 @@ update_cursor(term_T *term, int redraw) { if (term->tl_normal_mode) 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 (term->tl_buffer == curbuf && term->tl_cursor_visible) @@ -867,6 +914,15 @@ write_to_term(buf_T *buffer, char_u *msg ch_log(channel, "writing %d bytes to terminal", (int)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 * contents, thus no screen update is needed. */ if (!term->tl_normal_mode) @@ -1905,11 +1961,15 @@ terminal_loop(int blocking) while (blocking || vpeekc_nomap() != NUL) { - /* TODO: skip screen update when handling a sequence of keys. */ - /* Repeat redrawing in case a message is received while redrawing. */ - while (must_redraw != 0) - if (update_screen(0) == FAIL) - break; +#ifdef FEAT_GUI + if (!curbuf->b_term->tl_system) +#endif + /* TODO: skip screen update when handling a sequence of keys. */ + /* 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); restore_cursor = TRUE; @@ -2586,6 +2646,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. * Returns FAIL when there is no terminal running in this window or in * Terminal-Normal mode. @@ -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 && 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) { - 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; - } - } + int max_col = MIN(wp->w_width, term->tl_cols); + + term_line2screenline(screen, &pos, max_col); } else pos.col = 0; @@ -3623,7 +3737,7 @@ term_load_dump(typval_T *argvars, typval /* TODO: use the file name arguments for the buffer name */ 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) { int i; @@ -4396,7 +4510,7 @@ f_term_start(typval_T *argvars, typval_T if (opt.jo_vertical) 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) rettv->vval.v_number = buf->b_fnum; @@ -4592,6 +4706,7 @@ dyn_winpty_init(int verbose) term_and_job_init( term_T *term, typval_T *argvar, + char **argv UNUSED, jobopt_T *opt) { WCHAR *cmd_wchar = NULL; @@ -4880,18 +4995,20 @@ terminal_enabled(void) * Create a new terminal of "rows" by "cols" cells. * Start job for "cmd". * Store the pointers in "term". + * When "argv" is not NULL then "argvar" is not used. * Return OK or FAIL. */ static int term_and_job_init( term_T *term, typval_T *argvar, + char **argv, jobopt_T *opt) { create_vterm(term, term->tl_rows, term->tl_cols); - /* This will change a string in "argvar". */ - term->tl_job = job_start(argvar, opt); + /* This may change a string in "argvar". */ + term->tl_job = job_start(argvar, argv, opt); if (term->tl_job != NULL) ++term->tl_job->jv_refcount; diff --git a/src/version.c b/src/version.c --- 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 */ /**/ + 1609, +/**/ 1608, /**/ 1607, diff --git a/src/vim.h b/src/vim.h --- a/src/vim.h +++ b/src/vim.h @@ -2543,4 +2543,9 @@ typedef enum { #define REPLACE_CR_NCHAR -1 #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 */