To: vim_dev@googlegroups.com Subject: Patch 8.0.0813 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.0.0813 Problem: Cannot use Vim commands in a terminal window while the job is running. Solution: Implement Terminal Normal mode. Files: src/terminal.c, src/proto/terminal.pro, src/main.c, src/screen.c, src/normal.c, src/option.c, runtime/doc/terminal.txt *** ../vim-8.0.0812/src/terminal.c 2017-07-29 22:23:35.170665661 +0200 --- src/terminal.c 2017-07-30 16:49:16.171156264 +0200 *************** *** 36,48 **** * that buffer, attributes come from the scrollback buffer tl_scrollback. * * TODO: * - For the scrollback buffer store lines in the buffer, only attributes in * tl_scrollback. * - When the job ends: * - Need an option or argument to drop the window+buffer right away, to be ! * used for a shell or Vim. * - To set BS correctly, check get_stty(); Pass the fd of the pty. ! * - do not store terminal buffer in viminfo. Or prefix term:// ? * - add a character in :ls output * - when closing window and job has not ended, make terminal hidden? * - when closing window and job has ended, make buffer hidden? --- 36,58 ---- * that buffer, attributes come from the scrollback buffer tl_scrollback. * * TODO: + * - Problem with statusline (Zyx, Christian) + * - Make CTRL-W "" paste register content to the job? + * - in bash mouse clicks are inserting characters. + * - mouse scroll: when over other window, scroll that window. * - For the scrollback buffer store lines in the buffer, only attributes in * tl_scrollback. + * - Add term_status(): "" if not a terminal, "running" if job running, + * "finished" if finished, "running,vim" when job is running and in + * Terminal mode, "running,vim,pending" when job output is pending. * - When the job ends: * - Need an option or argument to drop the window+buffer right away, to be ! * used for a shell or Vim. 'termfinish'; "close", "open" (open window when ! * job finishes). ! * - add option values to the command: ! * :term <24x80> vim notes.txt * - To set BS correctly, check get_stty(); Pass the fd of the pty. ! * - do not store terminal window in viminfo. Or prefix term:// ? * - add a character in :ls output * - when closing window and job has not ended, make terminal hidden? * - when closing window and job has ended, make buffer hidden? *************** *** 53,58 **** --- 63,70 ---- * - support minimal size when 'termsize' is empty? * - implement "term" for job_start(): more job options when starting a * terminal. + * - if the job in the terminal does not support the mouse, we can use the + * mouse in the Terminal window for copy/paste. * - when 'encoding' is not utf-8, or the job is using another encoding, setup * conversions. * - In the GUI use a terminal emulator for :!cmd. *************** *** 78,90 **** struct terminal_S { term_T *tl_next; #ifdef WIN3264 void *tl_winpty_config; void *tl_winpty; #endif - VTerm *tl_vterm; - job_T *tl_job; - buf_T *tl_buffer; /* last known vterm size */ int tl_rows; --- 90,106 ---- struct terminal_S { term_T *tl_next; + VTerm *tl_vterm; + job_T *tl_job; + buf_T *tl_buffer; + + int tl_terminal_mode; + int tl_channel_closed; + #ifdef WIN3264 void *tl_winpty_config; void *tl_winpty; #endif /* last known vterm size */ int tl_rows; *************** *** 553,558 **** --- 569,773 ---- } /* + * Add the last line of the scrollback buffer to the buffer in the window. + */ + static void + add_scrollback_line_to_buffer(term_T *term) + { + linenr_T lnum = term->tl_scrollback.ga_len - 1; + sb_line_T *line = (sb_line_T *)term->tl_scrollback.ga_data + lnum; + garray_T ga; + int c; + int col; + int i; + + ga_init2(&ga, 1, 100); + for (col = 0; col < line->sb_cols; col += line->sb_cells[col].width) + { + if (ga_grow(&ga, MB_MAXBYTES) == FAIL) + goto failed; + for (i = 0; (c = line->sb_cells[col].chars[i]) > 0 || i == 0; ++i) + ga.ga_len += mb_char2bytes(c == NUL ? ' ' : c, + (char_u *)ga.ga_data + ga.ga_len); + } + if (ga_grow(&ga, 1) == FAIL) + goto failed; + *((char_u *)ga.ga_data + ga.ga_len) = NUL; + ml_append_buf(term->tl_buffer, lnum, ga.ga_data, ga.ga_len + 1, FALSE); + + if (lnum == 0) + { + /* Delete the empty line that was in the empty buffer. */ + curbuf = term->tl_buffer; + ml_delete(2, FALSE); + curbuf = curwin->w_buffer; + } + + failed: + ga_clear(&ga); + } + + /* + * Add the current lines of the terminal to scrollback and to the buffer. + * Called after the job has ended and when switching to Terminal mode. + */ + static void + move_terminal_to_buffer(term_T *term) + { + win_T *wp; + int len; + int lines_skipped = 0; + VTermPos pos; + VTermScreenCell cell; + VTermScreenCell *p; + VTermScreen *screen = vterm_obtain_screen(term->tl_vterm); + + for (pos.row = 0; pos.row < term->tl_rows; ++pos.row) + { + len = 0; + for (pos.col = 0; pos.col < term->tl_cols; ++pos.col) + if (vterm_screen_get_cell(screen, pos, &cell) != 0 + && cell.chars[0] != NUL) + len = pos.col + 1; + + if (len == 0) + ++lines_skipped; + else + { + while (lines_skipped > 0) + { + /* Line was skipped, add an empty line. */ + --lines_skipped; + if (ga_grow(&term->tl_scrollback, 1) == OK) + { + sb_line_T *line = (sb_line_T *)term->tl_scrollback.ga_data + + term->tl_scrollback.ga_len; + + line->sb_cols = 0; + line->sb_cells = NULL; + ++term->tl_scrollback.ga_len; + + add_scrollback_line_to_buffer(term); + } + } + + p = (VTermScreenCell *)alloc((int)sizeof(VTermScreenCell) * len); + if (p != NULL && ga_grow(&term->tl_scrollback, 1) == OK) + { + sb_line_T *line = (sb_line_T *)term->tl_scrollback.ga_data + + term->tl_scrollback.ga_len; + + for (pos.col = 0; pos.col < len; ++pos.col) + { + if (vterm_screen_get_cell(screen, pos, &cell) == 0) + vim_memset(p + pos.col, 0, sizeof(cell)); + else + p[pos.col] = cell; + } + line->sb_cols = len; + line->sb_cells = p; + ++term->tl_scrollback.ga_len; + + add_scrollback_line_to_buffer(term); + } + else + vim_free(p); + } + } + + FOR_ALL_WINDOWS(wp) + { + if (wp->w_buffer == term->tl_buffer) + { + wp->w_cursor.lnum = term->tl_buffer->b_ml.ml_line_count; + wp->w_cursor.col = 0; + wp->w_valid = 0; + redraw_win_later(wp, NOT_VALID); + } + } + } + + static void + set_terminal_mode(term_T *term, int on) + { + term->tl_terminal_mode = on; + vim_free(term->tl_status_text); + term->tl_status_text = NULL; + if (term->tl_buffer == curbuf) + maketitle(); + } + + /* + * Called after the job if finished and Terminal mode is not active: + * Move the vterm contents into the scrollback buffer and free the vterm. + */ + static void + cleanup_vterm(term_T *term) + { + move_terminal_to_buffer(term); + term_free_vterm(term); + set_terminal_mode(term, FALSE); + } + + /* + * Switch from sending keys to the job to Terminal-Normal mode. + * Suspends updating the terminal window. + */ + static void + term_enter_terminal_mode() + { + term_T *term = curbuf->b_term; + + /* Append the current terminal contents to the buffer. */ + move_terminal_to_buffer(term); + + set_terminal_mode(term, TRUE); + } + + /* + * Returns TRUE if the current window contains a terminal and we are in + * Terminal-Normal mode. + */ + int + term_in_terminal_mode() + { + term_T *term = curbuf->b_term; + + return term != NULL && term->tl_terminal_mode; + } + + /* + * Switch from Terminal-Normal mode to sending keys to the job. + * Restores updating the terminal window. + */ + void + term_leave_terminal_mode() + { + term_T *term = curbuf->b_term; + sb_line_T *line; + garray_T *gap; + + /* Remove the terminal contents from the scrollback and the buffer. */ + gap = &term->tl_scrollback; + while (curbuf->b_ml.ml_line_count > term->tl_scrollback_scrolled) + { + ml_delete(curbuf->b_ml.ml_line_count, FALSE); + line = (sb_line_T *)gap->ga_data + gap->ga_len - 1; + vim_free(line->sb_cells); + --gap->ga_len; + if (gap->ga_len == 0) + break; + } + check_cursor(); + + set_terminal_mode(term, FALSE); + + if (term->tl_channel_closed) + cleanup_vterm(term); + redraw_buf_and_status_later(curbuf, NOT_VALID); + } + + /* * Get a key from the user without mapping. * TODO: use terminal mode mappings. */ *************** *** 641,646 **** --- 856,876 ---- } /* + * Returns TRUE if the current window contains a terminal and we are sending + * keys to the job. + */ + int + term_use_loop() + { + term_T *term = curbuf->b_term; + + return term != NULL + && !term->tl_terminal_mode + && term->tl_vterm != NULL + && term_job_running(term); + } + + /* * Wait for input and send it to the job. * Return when the start of a CTRL-W command is typed or anything else that * should be handled as a Normal mode command. *************** *** 653,662 **** int c; int termkey = 0; - if (curbuf->b_term->tl_vterm == NULL || !term_job_running(curbuf->b_term)) - /* job finished */ - return OK; - if (*curwin->w_p_tk != NUL) termkey = string_to_key(curwin->w_p_tk, TRUE); --- 883,888 ---- *************** *** 665,670 **** --- 891,897 ---- /* TODO: skip screen update when handling a sequence of keys. */ update_screen(0); update_cursor(curbuf->b_term, FALSE); + c = term_vgetc(); if (curbuf->b_term->tl_vterm == NULL || !term_job_running(curbuf->b_term)) *************** *** 687,694 **** --- 914,928 ---- break; if (termkey == 0 && c == '.') + { /* "CTRL-W .": send CTRL-W to the job */ c = Ctrl_W; + } + else if (termkey == 0 && c == 'N') + { + term_enter_terminal_mode(); + return FAIL; + } else if (termkey == 0 || c != termkey) { stuffcharReadbuff(Ctrl_W); *************** *** 704,709 **** --- 938,945 ---- /* * Called when a job has finished. + * This updates the title and status, but does not close the vter, because + * there might still be pending output in the channel. */ void term_job_ended(job_T *job) *************** *** 891,1008 **** line->sb_cells = p; ++term->tl_scrollback.ga_len; ++term->tl_scrollback_scrolled; - } - return 0; /* ignored */ - } - - /* - * Fill the buffer with the scrollback lines and current lines of the terminal. - * Called after the job has ended. - */ - static void - move_scrollback_to_buffer(term_T *term) - { - linenr_T lnum; - garray_T ga; - int c; - int col; - int i; - win_T *wp; - int len; - int lines_skipped = 0; - VTermPos pos; - VTermScreenCell cell; - VTermScreenCell *p; - VTermScreen *screen = vterm_obtain_screen(term->tl_vterm); - - /* Append the the visible lines to the scrollback. */ - for (pos.row = 0; pos.row < term->tl_rows; ++pos.row) - { - len = 0; - for (pos.col = 0; pos.col < term->tl_cols; ++pos.col) - if (vterm_screen_get_cell(screen, pos, &cell) != 0 - && cell.chars[0] != NUL) - len = pos.col + 1; - - if (len == 0) - ++lines_skipped; - else - { - while (lines_skipped > 0) - { - /* Line was skipped, add an empty line. */ - --lines_skipped; - if (ga_grow(&term->tl_scrollback, 1) == OK) - { - sb_line_T *line = (sb_line_T *)term->tl_scrollback.ga_data - + term->tl_scrollback.ga_len; - - line->sb_cols = 0; - line->sb_cells = NULL; - ++term->tl_scrollback.ga_len; - } - } - - p = (VTermScreenCell *)alloc((int)sizeof(VTermScreenCell) * len); - if (p != NULL && ga_grow(&term->tl_scrollback, 1) == OK) - { - sb_line_T *line = (sb_line_T *)term->tl_scrollback.ga_data - + term->tl_scrollback.ga_len; - - for (pos.col = 0; pos.col < len; ++pos.col) - { - if (vterm_screen_get_cell(screen, pos, &cell) == 0) - vim_memset(p + pos.col, 0, sizeof(cell)); - else - p[pos.col] = cell; - } - line->sb_cols = len; - line->sb_cells = p; - ++term->tl_scrollback.ga_len; - } - else - vim_free(p); - } - } - - /* Add the text to the buffer. */ - ga_init2(&ga, 1, 100); - for (lnum = 0; lnum < term->tl_scrollback.ga_len; ++lnum) - { - sb_line_T *line = (sb_line_T *)term->tl_scrollback.ga_data + lnum; - - ga.ga_len = 0; - for (col = 0; col < line->sb_cols; ++col) - { - if (ga_grow(&ga, MB_MAXBYTES) == FAIL) - goto failed; - for (i = 0; (c = line->sb_cells[col].chars[i]) > 0 || i == 0; ++i) - ga.ga_len += mb_char2bytes(c == NUL ? ' ' : c, - (char_u *)ga.ga_data + ga.ga_len); - } - if (ga_grow(&ga, 1) == FAIL) - goto failed; - *((char_u *)ga.ga_data + ga.ga_len) = NUL; - ml_append_buf(term->tl_buffer, lnum, ga.ga_data, ga.ga_len + 1, FALSE); - } - - /* Delete the empty line that was in the empty buffer. */ - curbuf = term->tl_buffer; - ml_delete(lnum + 1, FALSE); - curbuf = curwin->w_buffer; - - failed: - ga_clear(&ga); ! FOR_ALL_WINDOWS(wp) ! { ! if (wp->w_buffer == term->tl_buffer) ! { ! wp->w_cursor.lnum = term->tl_buffer->b_ml.ml_line_count; ! wp->w_cursor.col = 0; ! wp->w_valid = 0; ! } } } static VTermScreenCallbacks screen_callbacks = { --- 1127,1136 ---- line->sb_cells = p; ++term->tl_scrollback.ga_len; ++term->tl_scrollback_scrolled; ! add_scrollback_line_to_buffer(term); } + return 0; /* ignored */ } static VTermScreenCallbacks screen_callbacks = { *************** *** 1029,1042 **** for (term = first_term; term != NULL; term = term->tl_next) if (term->tl_job == ch->ch_job) { vim_free(term->tl_title); term->tl_title = NULL; vim_free(term->tl_status_text); term->tl_status_text = NULL; ! /* move the lines into the buffer and free the vterm */ ! move_scrollback_to_buffer(term); ! term_free_vterm(term); redraw_buf_and_status_later(term->tl_buffer, NOT_VALID); did_one = TRUE; --- 1157,1172 ---- for (term = first_term; term != NULL; term = term->tl_next) if (term->tl_job == ch->ch_job) { + term->tl_channel_closed = TRUE; + vim_free(term->tl_title); term->tl_title = NULL; vim_free(term->tl_status_text); term->tl_status_text = NULL; ! /* Unless in Terminal-Normal mode: clear the vterm. */ ! if (!term->tl_terminal_mode) ! cleanup_vterm(term); redraw_buf_and_status_later(term->tl_buffer, NOT_VALID); did_one = TRUE; *************** *** 1227,1234 **** VTermState *state; VTermPos pos; ! if (term == NULL || term->tl_vterm == NULL) return FAIL; vterm = term->tl_vterm; screen = vterm_obtain_screen(vterm); state = vterm_obtain_state(vterm); --- 1357,1365 ---- VTermState *state; VTermPos pos; ! if (term == NULL || term->tl_vterm == NULL || term->tl_terminal_mode) return FAIL; + vterm = term->tl_vterm; screen = vterm_obtain_screen(vterm); state = vterm_obtain_state(vterm); *************** *** 1347,1352 **** --- 1478,1495 ---- } /* + * Return TRUE if "wp" is a terminal window where the job has finished or we + * are in Terminal-Normal mode. + */ + int + term_show_buffer(buf_T *buf) + { + term_T *term = buf->b_term; + + return term != NULL && (term->tl_vterm == NULL || term->tl_terminal_mode); + } + + /* * The current buffer is going to be changed. If there is terminal * highlighting remove it now. */ *************** *** 1450,1456 **** 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"); --- 1593,1606 ---- char_u *txt; size_t len; ! if (term->tl_terminal_mode) ! { ! if (term_job_running(term)) ! txt = (char_u *)_("Terminal"); ! else ! txt = (char_u *)_("Terminal-finished"); ! } ! else if (term->tl_title != NULL) txt = term->tl_title; else if (term_job_running(term)) txt = (char_u *)_("running"); *** ../vim-8.0.0812/src/proto/terminal.pro 2017-07-29 20:07:00.764940487 +0200 --- src/proto/terminal.pro 2017-07-30 15:59:29.864818283 +0200 *************** *** 2,12 **** --- 2,16 ---- void ex_terminal(exarg_T *eap); void free_terminal(buf_T *buf); void write_to_term(buf_T *buffer, char_u *msg, channel_T *channel); + int term_in_terminal_mode(void); + void term_leave_terminal_mode(void); + int term_use_loop(void); int terminal_loop(void); void term_job_ended(job_T *job); void term_channel_closed(channel_T *ch); int term_update_window(win_T *wp); int term_is_finished(buf_T *buf); + int term_show_buffer(buf_T *buf); void term_change_in_curbuf(void); int term_get_attr(buf_T *buf, linenr_T lnum, int col); char_u *term_get_status_text(term_T *term); *************** *** 16,23 **** void f_term_getline(typval_T *argvars, typval_T *rettv); void f_term_getsize(typval_T *argvars, typval_T *rettv); void f_term_list(typval_T *argvars, typval_T *rettv); - void f_term_start(typval_T *argvars, typval_T *rettv); void f_term_scrape(typval_T *argvars, typval_T *rettv); void f_term_sendkeys(typval_T *argvars, typval_T *rettv); void f_term_wait(typval_T *argvars, typval_T *rettv); /* vim: set ft=c : */ --- 20,27 ---- void f_term_getline(typval_T *argvars, typval_T *rettv); void f_term_getsize(typval_T *argvars, typval_T *rettv); void f_term_list(typval_T *argvars, typval_T *rettv); void f_term_scrape(typval_T *argvars, typval_T *rettv); void f_term_sendkeys(typval_T *argvars, typval_T *rettv); + void f_term_start(typval_T *argvars, typval_T *rettv); void f_term_wait(typval_T *argvars, typval_T *rettv); /* vim: set ft=c : */ *** ../vim-8.0.0812/src/main.c 2017-07-16 20:13:22.265843572 +0200 --- src/main.c 2017-07-30 15:47:01.714210944 +0200 *************** *** 1356,1366 **** else { #ifdef FEAT_TERMINAL ! if (curbuf->b_term != NULL && oa.op_type == OP_NOP ! && oa.regname == NUL) ! terminal_loop(); #endif ! normal_cmd(&oa, TRUE); } } } --- 1356,1372 ---- else { #ifdef FEAT_TERMINAL ! if (term_use_loop() && oa.op_type == OP_NOP && oa.regname == NUL) ! { ! /* If terminal_loop() returns OK we got a key that is handled ! * in Normal model. With FAIL the terminal was closed and the ! * screen needs to be redrawn. */ ! if (terminal_loop() == OK) ! normal_cmd(&oa, TRUE); ! } ! else #endif ! normal_cmd(&oa, TRUE); } } } *** ../vim-8.0.0812/src/screen.c 2017-07-30 13:57:37.616783276 +0200 --- src/screen.c 2017-07-30 15:43:01.783920068 +0200 *************** *** 3245,3251 **** #endif #ifdef FEAT_TERMINAL ! if (term_is_finished(wp->w_buffer)) { extra_check = TRUE; get_term_attr = TRUE; --- 3245,3251 ---- #endif #ifdef FEAT_TERMINAL ! if (term_show_buffer(wp->w_buffer)) { extra_check = TRUE; get_term_attr = TRUE; *** ../vim-8.0.0812/src/normal.c 2017-07-16 20:13:22.269843541 +0200 --- src/normal.c 2017-07-30 15:59:33.516791950 +0200 *************** *** 9037,9042 **** --- 9037,9050 ---- static void nv_edit(cmdarg_T *cap) { + #ifdef FEAT_TERMINAL + if (term_in_terminal_mode()) + { + term_leave_terminal_mode(); + return; + } + #endif + /* is equal to "i" */ if (cap->cmdchar == K_INS || cap->cmdchar == K_KINS) cap->cmdchar = 'i'; *** ../vim-8.0.0812/src/option.c 2017-07-28 13:48:15.398855045 +0200 --- src/option.c 2017-07-30 15:57:41.817597536 +0200 *************** *** 8222,8233 **** } #endif - #ifdef FEAT_TITLE /* when 'modifiable' is changed, redraw the window title */ else if ((int *)varp == &curbuf->b_p_ma) { redraw_titles(); } /* when 'endofline' is changed, redraw the window title */ else if ((int *)varp == &curbuf->b_p_eol) { --- 8222,8243 ---- } #endif /* when 'modifiable' is changed, redraw the window title */ else if ((int *)varp == &curbuf->b_p_ma) { + # ifdef FEAT_TERMINAL + /* Cannot set 'modifiable' when in Terminal mode. */ + if (term_in_terminal_mode()) + { + curbuf->b_p_ma = FALSE; + return (char_u *)N_("E946: Cannot make a terminal with running job modifiable"); + } + # endif + # ifdef FEAT_TITLE redraw_titles(); + # endif } + #ifdef FEAT_TITLE /* when 'endofline' is changed, redraw the window title */ else if ((int *)varp == &curbuf->b_p_eol) { *** ../vim-8.0.0812/runtime/doc/terminal.txt 2017-07-28 13:48:15.398855045 +0200 --- runtime/doc/terminal.txt 2017-07-30 16:45:34.584766890 +0200 *************** *** 1,4 **** ! *terminal.txt* For Vim version 8.0. Last change: 2017 Jul 28 VIM REFERENCE MANUAL by Bram Moolenaar --- 1,4 ---- ! *terminal.txt* For Vim version 8.0. Last change: 2017 Jul 30 VIM REFERENCE MANUAL by Bram Moolenaar *************** *** 33,56 **** The job runs asynchronously from Vim, the window will be updated to show output from the job, also while editing in any other window. Typing ~ When the keyboard focus is in the terminal window, typed keys will be send to the job. This uses a pty when possible. You can click outside of the terminal window to move keyboard focus elsewhere. ! Navigate between windows with CTRL-W commands. E.g. CTRL-W CTRL-W moves focus ! to the next window. Use "CTRL-W :" to edit an Ex command. Use "CTRL-W ." to ! send a CTRL-W to the job in the terminal. - See option 'termkey' for specifying another key that precedes a Vim command. - Typing 'termkey' twice sends 'termkey' to the job. Size ~ See option 'termsize' for controlling the size of the terminal window. (TODO: scrolling when the terminal is larger than the window) Syntax ~ :ter[minal] [command] *:ter* *:terminal* --- 33,71 ---- The job runs asynchronously from Vim, the window will be updated to show output from the job, also while editing in any other window. + Typing ~ When the keyboard focus is in the terminal window, typed keys will be send to the job. This uses a pty when possible. You can click outside of the terminal window to move keyboard focus elsewhere. ! CTRL-W can be used to navigate between windows and other CTRL-W commands, e.g.: ! CTRL-W CTRL-W move focus to the next window ! CTRL-W : enter an Ex command ! See |CTRL-W| for more commands. ! ! Special in the terminal window: *CTRL-W_.* *CTRL-W_N* ! CTRL-W . send a CTRL-W to the job in the terminal ! CTRL-W N go to Terminal Normal mode, see |Terminal-mode| ! ! See option 'termkey' for specifying another key instead of CTRL-W that ! will work like CTRL-W. However, typing 'termkey' twice sends 'termkey' to ! the job. For example: ! 'termkey' CTRL-W move focus to the next window ! 'termkey' : enter an Ex command ! 'termkey' 'termkey' send 'termkey' to the job in the terminal ! 'termkey' . send a CTRL-W to the job in the terminal ! 'termkey' N go to terminal Normal mode, see below ! 'termkey' CTRL-N same as CTRL-W N Size ~ See option 'termsize' for controlling the size of the terminal window. (TODO: scrolling when the terminal is larger than the window) + Syntax ~ :ter[minal] [command] *:ter* *:terminal* *************** *** 99,104 **** --- 114,138 ---- not when 'termsize' is "rowsXcols". + Terminal Normal mode ~ + *Terminal-mode* + When the job is running the contents of the terminal is under control of the + job. That includes the cursor position. The terminal contents can change at + any time. + + Use CTRL-W N (or 'termkey' N) to go to Terminal Normal mode. Now the contents + of the terminal window is under control of Vim, the job output is suspended. + *E946* + In this mode you can move the cursor around with the usual Vim commands, + Visually mark text, yank text, etc. But you cannot change the contents of the + buffer. The commands that would start insert mode, such as 'i' and 'a', + return control of the window to the job. Any pending output will now be + displayed. + + In Terminal mode the statusline and window title show "(Terminal)". If the + job ends while in Terminal mode this changes to "(Terminal-finished)". + + Unix ~ On Unix a pty is used to make it possible to run all kinds of commands. You *** ../vim-8.0.0812/src/version.c 2017-07-30 13:57:37.616783276 +0200 --- src/version.c 2017-07-30 16:43:17.709763223 +0200 *************** *** 771,772 **** --- 771,774 ---- { /* Add new patch number below this line */ + /**/ + 813, /**/ -- TALL KNIGHT: We shall say Ni! again to you if you do not appease us. ARTHUR: All right! What do you want? TALL KNIGHT: We want ... a shrubbery! "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///