diff --git a/runtime/doc/terminal.txt b/runtime/doc/terminal.txt index 4fc119cc7f..73bcf99719 100644 --- a/runtime/doc/terminal.txt +++ b/runtime/doc/terminal.txt @@ -1,4 +1,4 @@ -*terminal.txt* For Vim version 8.0. Last change: 2017 Jul 19 +*terminal.txt* For Vim version 8.0. Last change: 2017 Jul 24 VIM REFERENCE MANUAL by Bram Moolenaar @@ -47,8 +47,9 @@ See option 'termsize' for controlling the size of the terminal window. (TODO: scrolling when the terminal is larger than the window) Syntax ~ - *:ter* *:terminal* -:terminal[!] [command] Open a new terminal window. + +:ter[minal][!] [command] *:ter* *:terminal* + Open a new terminal window. If [command] is provided run it as a job and connect the input and output to the terminal. @@ -79,7 +80,7 @@ The size of the terminal can be in one of three modes: The minimal size is 2 screen lines with 10 cells. 2. The 'termsize' option is "rows*cols", where "rows" is the minimal number of - screen rows and "cols" is the minial number of cells. + screen rows and "cols" is the minimal number of cells. 3. The 'termsize' option is "rowsXcols" (where the x is upper or lower case). The terminal size is fixed to the specified number of screen lines and @@ -101,9 +102,20 @@ can even run Vim in the terminal! That's used for debugging, see below. MS-Windows ~ -On MS-Windows a hidden console is used to run the command in. This should -work well for all kind of commands. Obviously, they must be commands that run -in a terminal, not open their own window. +On MS-Windows winpty is used to make it possible to run all kind of commands. +Obviously, they must be commands that run in a terminal, not open their own +window. + +You need the following two files from winpty: + + winpty.dll + winpty-agent.exe + +You can download them from the following page: + + https://github.com/rprichard/winpty + +Just put the files somewhere in your PATH. ============================================================================== 2. Remote testing *terminal-testing* diff --git a/src/INSTALLpc.txt b/src/INSTALLpc.txt index b875b77c9a..762fdb6010 100644 --- a/src/INSTALLpc.txt +++ b/src/INSTALLpc.txt @@ -706,20 +706,17 @@ Or when using MinGW (as one line): 13. Building with Terminal support ================================== -Vim with Terminal support can be built with either MSVC, or MinGW or Cygwin. +Vim with Terminal support can be built with either MSVC, MinGW or Cygwin. This uses the included libvterm and winpty. No extra header files or -libraries are needed for building. +libraries are needed for building. Just set TERMINAL to yes. -Running Vim with terminal support requires the following two winpty files: +E.g. When using MSVC: - winpty.dll - winpty-agent.dll + nmake -f Make_mvc.mak TERMINAL=yes -You can download them from the following page: +Or when using MinGW (as one line): - https://github.com/rprichard/winpty - -Just put the DLL files somewhere in your PATH. + mingw32-make -f Make_mingw.mak TERMINAL=yes 14. Windows 3.1x diff --git a/src/Make_cyg_ming.mak b/src/Make_cyg_ming.mak index 0f6a492188..a7bf3b90b9 100644 --- a/src/Make_cyg_ming.mak +++ b/src/Make_cyg_ming.mak @@ -73,6 +73,7 @@ CHANNEL=yes else CHANNEL=$(GUI) endif +# Set to yes to enable terminal support. TERMINAL=no diff --git a/src/Make_mvc.mak b/src/Make_mvc.mak index 99dcb294dc..d51d9762a2 100644 --- a/src/Make_mvc.mak +++ b/src/Make_mvc.mak @@ -356,6 +356,9 @@ CSCOPE_DEFS = -DFEAT_CSCOPE !if "$(TERMINAL)" == "yes" TERMINAL_OBJ = $(OBJDIR)/terminal.obj TERMINAL_DEFS = -DFEAT_TERMINAL +!if $(MSVC_MAJOR) <= 11 +TERMINAL_DEFS = $(TERMINAL_DEFS) /I if_perl_msvc +!endif TERMINAL_SRC = terminal.c VTERM_LIB = libvterm/vterm.lib !endif @@ -1154,7 +1157,7 @@ all: $(VIM).exe \ $(VIM).exe: $(OUTDIR) $(OBJ) $(GUI_OBJ) $(CUI_OBJ) $(OLE_OBJ) $(OLE_IDL) $(MZSCHEME_OBJ) \ $(LUA_OBJ) $(PERL_OBJ) $(PYTHON_OBJ) $(PYTHON3_OBJ) $(RUBY_OBJ) $(TCL_OBJ) \ - $(CSCOPE_OBJ) $(TERMINAL_OBJ) $(NETBEANS_OBJ) $(CHANNEL_OBJ) $(XPM_OBJ) \ + $(CSCOPE_OBJ) $(TERMINAL_OBJ) $(NETBEANS_OBJ) $(CHANNEL_OBJ) $(XPM_OBJ) $(VTERM_LIB) \ version.c version.h $(CC) $(CFLAGS) version.c $(link) $(LINKARGS1) -out:$(VIM).exe $(OBJ) $(GUI_OBJ) $(CUI_OBJ) $(OLE_OBJ) \ @@ -1549,7 +1552,7 @@ proto.h: \ libvterm/vterm.lib : cd libvterm - $(MAKE) /NOLOGO -f Makefile.msc + $(MAKE) /NOLOGO -f Makefile.msc "MSVC_MAJOR=$(MSVC_MAJOR)" cd .. # vim: set noet sw=8 ts=8 sts=0 wm=0 tw=0: diff --git a/src/buffer.c b/src/buffer.c index 126c106f48..b0524cd382 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -3634,6 +3634,13 @@ maketitle(void) #define SPACE_FOR_ARGNR (IOSIZE - 10) /* at least room for " - VIM" */ if (curbuf->b_fname == NULL) vim_strncpy(buf, (char_u *)_("[No Name]"), SPACE_FOR_FNAME); +#ifdef FEAT_TERMINAL + else if (curbuf->b_term != NULL) + { + vim_strncpy(buf, term_get_status_text(curbuf->b_term), + SPACE_FOR_FNAME); + } +#endif else { p = transstr(gettail(curbuf->b_fname)); @@ -3641,20 +3648,27 @@ maketitle(void) vim_free(p); } - switch (bufIsChanged(curbuf) - + (curbuf->b_p_ro * 2) - + (!curbuf->b_p_ma * 4)) - { - case 1: STRCAT(buf, " +"); break; - case 2: STRCAT(buf, " ="); break; - case 3: STRCAT(buf, " =+"); break; - case 4: - case 6: STRCAT(buf, " -"); break; - case 5: - case 7: STRCAT(buf, " -+"); break; - } +#ifdef FEAT_TERMINAL + if (curbuf->b_term == NULL) +#endif + switch (bufIsChanged(curbuf) + + (curbuf->b_p_ro * 2) + + (!curbuf->b_p_ma * 4)) + { + case 1: STRCAT(buf, " +"); break; + case 2: STRCAT(buf, " ="); break; + case 3: STRCAT(buf, " =+"); break; + case 4: + case 6: STRCAT(buf, " -"); break; + case 5: + case 7: STRCAT(buf, " -+"); break; + } - if (curbuf->b_fname != NULL) + if (curbuf->b_fname != NULL +#ifdef FEAT_TERMINAL + && curbuf->b_term == NULL +#endif + ) { /* Get path of file, replace home dir with ~ */ off = (int)STRLEN(buf); @@ -3671,18 +3685,8 @@ maketitle(void) p = gettail_sep(buf + off); if (p == buf + off) { - char *txt; - -#ifdef FEAT_TERMINAL - if (curbuf->b_term != NULL) - txt = term_job_running(curbuf) - ? _("running") : _("finished"); - else -#endif - txt = _("help"); - - /* must be a help or terminal buffer */ - vim_strncpy(buf + off, (char_u *)txt, + /* must be a help buffer */ + vim_strncpy(buf + off, (char_u *)_("help"), (size_t)(SPACE_FOR_DIR - off - 1)); } else @@ -5680,16 +5684,20 @@ buf_spname(buf_T *buf) return (char_u *)_(msg_qflist); } #endif -#ifdef FEAT_QUICKFIX + /* There is no _file_ when 'buftype' is "nofile", b_sfname - * contains the name as specified by the user */ + * contains the name as specified by the user. */ if (bt_nofile(buf)) { +#ifdef FEAT_TERMINAL + if (buf->b_term != NULL) + return term_get_status_text(buf->b_term); +#endif if (buf->b_sfname != NULL) return buf->b_sfname; return (char_u *)_("[Scratch]"); } -#endif + if (buf->b_fname == NULL) return (char_u *)_("[No Name]"); return NULL; diff --git a/src/gui_x11.c b/src/gui_x11.c index e0b91e65cd..855f4be0e9 100644 --- a/src/gui_x11.c +++ b/src/gui_x11.c @@ -2270,8 +2270,6 @@ fontset_ascent(XFontSet fs) gui_mch_get_color(char_u *name) { guicolor_T requested; - XColor available; - Colormap colormap; /* can't do this when GUI not running */ if (!gui.in_use || name == NULL || *name == NUL) @@ -2295,6 +2293,8 @@ gui_mch_get_color(char_u *name) gui_mch_get_rgb_color(int r, int g, int b) { char spec[8]; /* space enough to hold "#RRGGBB" */ + XColor available; + Colormap colormap; vim_snprintf(spec, sizeof(spec), "#%.2x%.2x%.2x", r, g, b); colormap = DefaultColormap(gui.dpy, DefaultScreen(gui.dpy)); diff --git a/src/libvterm/Makefile.msc b/src/libvterm/Makefile.msc index 18280d256d..c2313ba998 100644 --- a/src/libvterm/Makefile.msc +++ b/src/libvterm/Makefile.msc @@ -1,3 +1,5 @@ +CFLAGS = /DINLINE= /Iinclude + OBJS = \ src\encoding.c \ src\keyboard.c \ @@ -24,7 +26,10 @@ all : vterm.lib .c.obj : - cl /DINLINE= /Iinclude /Fo$@ /c $< + cl $(CFLAGS) /Fo$@ /c $< vterm.lib : $(OBJS) lib /OUT:$@ $(OBJS) + +clean: + del $(OBJS) vterm.lib diff --git a/src/libvterm/bin/vterm-ctrl.c b/src/libvterm/bin/vterm-ctrl.c index 14e35cda52..2568ee6090 100644 --- a/src/libvterm/bin/vterm-ctrl.c +++ b/src/libvterm/bin/vterm-ctrl.c @@ -1,10 +1,11 @@ #define _XOPEN_SOURCE 500 /* strdup */ -#include #include #include #include #define streq(a,b) (strcmp(a,b)==0) +#define TRUE 1 +#define FALSE 0 #include @@ -60,13 +61,14 @@ static char *helptext[] = { NULL }; -static bool seticanon(bool icanon, bool echo) +static int seticanon(int icanon, int echo) { struct termios termios; + int ret; tcgetattr(0, &termios); - bool ret = (termios.c_lflag & ICANON); + ret = (termios.c_lflag & ICANON); if(icanon) termios.c_lflag |= ICANON; else termios.c_lflag &= ~ICANON; @@ -84,16 +86,16 @@ static void await_c1(int c1) int c; /* await CSI - 8bit or 2byte 7bit form */ - bool in_esc = false; + int in_esc = FALSE; while((c = getchar())) { if(c == c1) break; if(in_esc && c == (char)(c1 - 0x40)) break; if(!in_esc && c == 0x1b) - in_esc = true; + in_esc = TRUE; else - in_esc = false; + in_esc = FALSE; } } @@ -121,7 +123,7 @@ static char *read_csi() static char *read_dcs() { unsigned char dcs[32]; - bool in_esc = false; + int in_esc = FALSE; int i; await_c1(0x90); @@ -133,10 +135,10 @@ static char *read_dcs() if(in_esc && c == 0x5c) break; if(!in_esc && c == 0x1b) - in_esc = true; + in_esc = TRUE; else { dcs[i++] = c; - in_esc = false; + in_esc = FALSE; } } dcs[++i] = 0; @@ -158,7 +160,7 @@ static void usage(int exitcode) exit(exitcode); } -static bool query_dec_mode(int mode) +static int query_dec_mode(int mode) { char *s = NULL; @@ -189,12 +191,12 @@ static bool query_dec_mode(int mode) free(s); if(reply_value == 1 || reply_value == 3) - return true; + return TRUE; if(reply_value == 2 || reply_value == 4) - return false; + return FALSE; printf("Unrecognised reply to DECRQM: %d\n", reply_value); - return false; + return FALSE; } while(1); } @@ -247,11 +249,11 @@ static int query_rqss_numeric(char *cmd) } while(1); } -bool wasicanon; +int wasicanon; void restoreicanon(void) { - seticanon(wasicanon, true); + seticanon(wasicanon, TRUE); } int main(int argc, char *argv[]) @@ -261,7 +263,7 @@ int main(int argc, char *argv[]) if(argc == 1) usage(0); - wasicanon = seticanon(false, false); + wasicanon = seticanon(FALSE, FALSE); atexit(restoreicanon); while(argi < argc) { diff --git a/src/option.c b/src/option.c index 27ed7dd20b..916360dfa4 100644 --- a/src/option.c +++ b/src/option.c @@ -7602,7 +7602,7 @@ did_set_string_option( #ifdef FEAT_TERMINAL /* 'termkey' */ - else if (varp == &curwin->w_p_tms) + else if (varp == &curwin->w_p_tk) { if (*curwin->w_p_tk != NUL && string_to_key(curwin->w_p_tk, TRUE) == 0) errmsg = e_invarg; diff --git a/src/proto/terminal.pro b/src/proto/terminal.pro index bdab8902b7..d7d153850c 100644 --- a/src/proto/terminal.pro +++ b/src/proto/terminal.pro @@ -4,6 +4,6 @@ void free_terminal(term_T *term); void write_to_term(buf_T *buffer, char_u *msg, channel_T *channel); void terminal_loop(void); void term_job_ended(job_T *job); -int term_job_running(buf_T *buf); void term_update_window(win_T *wp); +char_u *term_get_status_text(term_T *term); /* vim: set ft=c : */ diff --git a/src/terminal.c b/src/terminal.c index f2fae57895..93715d51a5 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -25,7 +25,7 @@ * the terminal emulator. * * If the terminal window has keyboard focus, typed keys are converted to the - * terminal encoding and writting to the job over a channel. + * terminal encoding and writing to the job over a channel. * * If the job produces output, it is written to the terminal emulator. The * terminal emulator invokes callbacks when its screen content changes. The @@ -34,7 +34,6 @@ * * TODO: * - do not store terminal buffer in viminfo - * - put terminal title in the statusline * - Add a scrollback buffer (contains lines to scroll off the top). * Can use the buf_T lines, store attributes somewhere else? * - When the job ends: @@ -43,6 +42,9 @@ * - Free the terminal emulator. * - Display the scrollback buffer (but with attributes). * Make the buffer not modifiable, drop attributes when making changes. + * - Need an option or argument to drop the window+buffer right away, to be + * used for a shell or Vim. + * - add a character in :ls output * - when closing window and job has not ended, make terminal hidden? * - don't allow exiting Vim when a terminal is still running a job * - use win_del_lines() to make scroll-up efficient. @@ -95,6 +97,9 @@ struct terminal_S { int tl_rows_fixed; int tl_cols_fixed; + char_u *tl_title; /* NULL or allocated */ + char_u *tl_status_text; /* NULL or allocated */ + /* Range of screen rows to update. Zero based. */ int tl_dirty_row_start; /* -1 if nothing dirty */ int tl_dirty_row_end; /* row below last one to update */ @@ -271,6 +276,8 @@ free_terminal(term_T *term) } term_free(term); + vim_free(term->tl_title); + vim_free(term->tl_status_text); vim_free(term); } @@ -527,6 +534,25 @@ terminal_loop(void) void term_job_ended(job_T *job) { + term_T *term; + int did_one = FALSE; + + for (term = first_term; term != NULL; term = term->tl_next) + if (term->tl_job == job) + { + vim_free(term->tl_title); + term->tl_title = NULL; + vim_free(term->tl_status_text); + term->tl_status_text = NULL; + redraw_buf_and_status_later(term->tl_buffer, VALID); + did_one = TRUE; + } + if (did_one) + { + redraw_statuslines(); + setcursor(); + out_flush(); + } if (curbuf->b_term != NULL && curbuf->b_term->tl_job == job) maketitle(); } @@ -534,11 +560,10 @@ term_job_ended(job_T *job) /* * Return TRUE if the job for "buf" is still running. */ - int -term_job_running(buf_T *buf) + static int +term_job_running(term_T *term) { - return buf->b_term != NULL && buf->b_term->tl_job != NULL - && buf->b_term->tl_job->jv_status == JOB_STARTED; + return term->tl_job != NULL && term->tl_job->jv_status == JOB_STARTED; } static void @@ -548,22 +573,6 @@ position_cursor(win_T *wp, VTermPos *pos) wp->w_wcol = MIN(pos->col, MAX(0, wp->w_width - 1)); } -static int handle_damage(VTermRect rect, void *user); -static int handle_moverect(VTermRect dest, VTermRect src, void *user); -static int handle_movecursor(VTermPos pos, VTermPos oldpos, int visible, void *user); -static int handle_resize(int rows, int cols, void *user); - -static VTermScreenCallbacks screen_callbacks = { - handle_damage, /* damage */ - handle_moverect, /* moverect */ - handle_movecursor, /* movecursor */ - NULL, /* settermprop */ - NULL, /* bell */ - handle_resize, /* resize */ - NULL, /* sb_pushline */ - NULL /* sb_popline */ -}; - static int handle_damage(VTermRect rect, void *user) { @@ -615,6 +624,30 @@ handle_movecursor( return 1; } + static int +handle_settermprop( + VTermProp prop, + VTermValue *value, + void *user) +{ + term_T *term = (term_T *)user; + + switch (prop) + { + case VTERM_PROP_TITLE: + vim_free(term->tl_title); + term->tl_title = vim_strsave((char_u *)value->string); + vim_free(term->tl_status_text); + term->tl_status_text = NULL; + if (term == curbuf->b_term) + maketitle(); + return 1; + default: + break; + } + return 0; +} + /* * The job running in the terminal resized the terminal. */ @@ -639,6 +672,17 @@ handle_resize(int rows, int cols, void *user) return 1; } +static VTermScreenCallbacks screen_callbacks = { + handle_damage, /* damage */ + handle_moverect, /* moverect */ + handle_movecursor, /* movecursor */ + handle_settermprop, /* settermprop */ + NULL, /* bell */ + handle_resize, /* resize */ + NULL, /* sb_pushline */ + NULL /* sb_popline */ +}; + /* * Reverse engineer the RGB value into a cterm color index. * First color is 1. Return 0 if no match found. @@ -687,7 +731,7 @@ color2index(VTermColor *color) else if (red == 128) { if (green == 128 && blue == 128) - return 9; /* high intensity bladk */ + return 9; /* high intensity black */ } else if (red == 255) { @@ -850,7 +894,10 @@ term_update_window(win_T *wp) if (c == NUL) { ScreenLines[off] = ' '; - ScreenLinesUC[off] = NUL; +#if defined(FEAT_MBYTE) + if (enc_utf8) + ScreenLinesUC[off] = NUL; +#endif } else { @@ -863,7 +910,8 @@ term_update_window(win_T *wp) else { ScreenLines[off] = c; - ScreenLinesUC[off] = NUL; + if (enc_utf8) + ScreenLinesUC[off] = NUL; } #else ScreenLines[off] = c; @@ -876,7 +924,10 @@ term_update_window(win_T *wp) if (cell.width == 2) { ScreenLines[off] = NUL; - ScreenLinesUC[off] = NUL; +#if defined(FEAT_MBYTE) + if (enc_utf8) + ScreenLinesUC[off] = NUL; +#endif ++pos.col; ++off; } @@ -950,14 +1001,40 @@ create_vterm(term_T *term, int rows, int cols) vterm_screen_reset(screen, 1 /* hard */); } +/* + * Return the text to show for the buffer name and status. + */ + char_u * +term_get_status_text(term_T *term) +{ + if (term->tl_status_text == NULL) + { + char_u *txt; + size_t len; + + if (term->tl_title != NULL) + txt = term->tl_title; + else if (term_job_running(term)) + txt = (char_u *)_("running"); + else + txt = (char_u *)_("finished"); + len = 9 + STRLEN(term->tl_buffer->b_fname) + STRLEN(txt); + term->tl_status_text = alloc(len); + if (term->tl_status_text != NULL) + vim_snprintf((char *)term->tl_status_text, len, "%s [%s]", + term->tl_buffer->b_fname, txt); + } + return term->tl_status_text; +} + # ifdef WIN3264 #define WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN 1ul #define WINPTY_SPAWN_FLAG_EXIT_AFTER_SHUTDOWN 2ull -void* (*winpty_config_new)(int, void*); +void* (*winpty_config_new)(UINT64, void*); void* (*winpty_open)(void*, void*); -void* (*winpty_spawn_config_new)(int, void*, LPCWSTR, void*, void*, void*); +void* (*winpty_spawn_config_new)(UINT64, void*, LPCWSTR, void*, void*, void*); BOOL (*winpty_spawn)(void*, void*, HANDLE*, HANDLE*, DWORD*, void*); void (*winpty_config_set_initial_size)(void*, int, int); LPCWSTR (*winpty_conin_name)(void*); diff --git a/src/version.c b/src/version.c index e11a3bd41e..d4aac3ee05 100644 --- a/src/version.c +++ b/src/version.c @@ -784,6 +784,14 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 769, +/**/ + 768, +/**/ + 767, +/**/ + 766, /**/ 765, /**/