From 696d00f488dc0599692993f226a7dd95a187920d Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 29 Jul 2017 14:52:43 +0200 Subject: [PATCH 01/24] patch 8.0.0800 Problem: Terminal window scrollback contents is wrong. Solution: Fix handling of multi-byte characters (Yasuhiro Matsumoto) Handle empty lines correctly. (closes #1891) --- src/terminal.c | 40 ++++++++++++++++++++++------------------ src/version.c | 2 ++ 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/terminal.c b/src/terminal.c index 9e14b4327c..b68081f314 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -765,26 +765,26 @@ handle_pushline(int cols, const VTermScreenCell *cells, void *user) /* TODO: put the text in the buffer. */ if (ga_grow(&term->tl_scrollback, 1) == OK) { - VTermScreenCell *p; - int len; - int i; + VTermScreenCell *p = NULL; + int len = 0; + int i; + sb_line_T *line; /* do not store empty cells at the end */ for (i = 0; i < cols; ++i) if (cells[i].chars[0] != 0) len = i + 1; - p = (VTermScreenCell *)alloc((int)sizeof(VTermScreenCell) * len); + if (len > 0) + p = (VTermScreenCell *)alloc((int)sizeof(VTermScreenCell) * len); if (p != NULL) - { - sb_line_T *line = (sb_line_T *)term->tl_scrollback.ga_data - + term->tl_scrollback.ga_len; - mch_memmove(p, cells, sizeof(VTermScreenCell) * len); - line->sb_cols = len; - line->sb_cells = p; - ++term->tl_scrollback.ga_len; - } + + line = (sb_line_T *)term->tl_scrollback.ga_data + + term->tl_scrollback.ga_len; + line->sb_cols = len; + line->sb_cells = p; + ++term->tl_scrollback.ga_len; } return 0; /* ignored */ } @@ -818,7 +818,9 @@ move_scrollback_to_buffer(term_T *term) && cell.chars[0] != NUL) len = pos.col + 1; - if (len > 0) + if (len == 0) + ++lines_skipped; + else { while (lines_skipped > 0) { @@ -865,13 +867,15 @@ move_scrollback_to_buffer(term_T *term) ga.ga_len = 0; for (col = 0; col < line->sb_cols; ++col) - for (i = 0; (c = line->sb_cells[col].chars[i]) != 0 || i == 0; ++i) - { - if (ga_grow(&ga, MB_MAXBYTES) == FAIL) - goto failed; + { + 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); } diff --git a/src/version.c b/src/version.c index d4926a8d01..a44e5505b9 100644 --- a/src/version.c +++ b/src/version.c @@ -769,6 +769,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 800, /**/ 799, /**/ From 1e8340bbbfb26d170a3f8b135dcb874e90a74e0b Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 29 Jul 2017 15:53:39 +0200 Subject: [PATCH 02/24] patch 8.0.0801: terminal window title says "running" after job exits Problem: The terminal window title sometimes still says "running" even though the job has finished. Solution: Also consider the job finished when the channel has been closed. --- src/terminal.c | 6 +++++- src/version.c | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/terminal.c b/src/terminal.c index b68081f314..d02e7f12c4 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -491,7 +491,11 @@ term_convert_key(int c, char *buf) static int term_job_running(term_T *term) { - return term->tl_job != NULL && term->tl_job->jv_status == JOB_STARTED; + /* Also consider the job finished when the channel is closed, to avoid a + * race condition when updating the title. */ + return term->tl_job != NULL + && term->tl_job->jv_status == JOB_STARTED + && channel_is_open(term->tl_job->jv_channel); } /* diff --git a/src/version.c b/src/version.c index a44e5505b9..4c2a48ad37 100644 --- a/src/version.c +++ b/src/version.c @@ -769,6 +769,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 801, /**/ 800, /**/ From 70229f951f00cdcff790f2e70b0b0a601202e9e7 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 29 Jul 2017 16:01:53 +0200 Subject: [PATCH 03/24] patch 8.0.0802: last line of terminal window has no color Problem: After a job exits the last line in the terminal window does not get color attributes. Solution: Fix off-by-one error. --- src/terminal.c | 2 +- src/version.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/terminal.c b/src/terminal.c index d02e7f12c4..3972bcc833 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -1270,7 +1270,7 @@ term_get_attr(buf_T *buf, linenr_T lnum, int col) term_T *term = buf->b_term; sb_line_T *line; - if (lnum >= term->tl_scrollback.ga_len) + if (lnum > term->tl_scrollback.ga_len) return 0; line = (sb_line_T *)term->tl_scrollback.ga_data + lnum - 1; if (col >= line->sb_cols) diff --git a/src/version.c b/src/version.c index 4c2a48ad37..44ae124a4f 100644 --- a/src/version.c +++ b/src/version.c @@ -769,6 +769,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 802, /**/ 801, /**/ From c6df10e5d33ffab2c392626e285317ea8241ebff Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 29 Jul 2017 20:15:08 +0200 Subject: [PATCH 04/24] patch 8.0.0804: terminal window functions not yet implemented Problem: Terminal window functions not yet implemented. Solution: Implement several functions. Add a first test. (Yasuhiro Matsumoto, closes #1871) --- runtime/doc/eval.txt | 90 +++++-- src/Makefile | 1 + src/evalfunc.c | 13 +- src/proto/evalfunc.pro | 1 + src/proto/terminal.pro | 10 + src/terminal.c | 485 +++++++++++++++++++++++++++++----- src/testdir/Make_all.mak | 1 + src/testdir/test_terminal.vim | 67 +++++ src/version.c | 2 + 9 files changed, 579 insertions(+), 91 deletions(-) create mode 100644 src/testdir/test_terminal.vim diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index ef02e6597c..9d26f361fa 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1,4 +1,4 @@ -*eval.txt* For Vim version 8.0. Last change: 2017 Jul 28 +*eval.txt* For Vim version 8.0. Last change: 2017 Jul 29 VIM REFERENCE MANUAL by Bram Moolenaar @@ -2369,12 +2369,15 @@ tagfiles() List tags files used tan({expr}) Float tangent of {expr} tanh({expr}) Float hyperbolic tangent of {expr} tempname() String name for a temporary file -term_getsize() Dict get the size of a terminal -term_open() Job open a terminal window and run a job -term_scrape() List inspect terminal screen -term_sendkeys() Number send keystrokes to a terminal -term_setsize() Number set the size of a terminal -term_wait() Number wait for screen to be updated +term_getattr({attr}, {what} Number get the value of attribute {what} +term_getjob({buf}) Job get the job associated with a terminal +term_getline({buf}, {row}) String get a line of text from a terminal +term_getsize({buf}) List get the size of a terminal +term_list() List get the list of terminal buffers +term_scrape({buf}, {row}) List get row of a terminal screen +term_sendkeys({buf}, {keys}) none send keystrokes to a terminal +term_start({cmd}, {options}) Job open a terminal window and run a job +term_wait({buf}) Number wait for screen to be updated test_alloc_fail({id}, {countdown}, {repeat}) none make memory allocation fail test_autochdir() none enable 'autochdir' during startup @@ -7898,23 +7901,72 @@ tempname() *tempname()* *temp-file-name* For MS-Windows forward slashes are used when the 'shellslash' option is set or when 'shellcmdflag' starts with '-'. -term_getsize() *term_getsize()* - Get the size of a terminal. NOT IMPLEMENTED YET +term_getattr({attr}, {what}) *term_getattr()* + Given {attr}, a value returned by term_scrape() in the "attr" + item, return whether {what} is on. {what} can be one of: + bold + italic + underline + strike + reverse -term_open() *term_open()* - Open a terminal window and run a job. NOT IMPLEMENTED YET +term_getjob({buf}) *term_getjob()* + Get the Job associated with terminal window {buf}. + {buf} is used as with |term_getsize()|. -term_scrape() *term_scrape()* - Inspect terminal screen. NOT IMPLEMENTED YET +term_getline({buf}, {row}) *term_getline()* + Get a line of text from the terminal window of {buf}. + {buf} is used as with |term_getsize()|. -term_sendkeys() *term_sendkeys()* - Send keystrokes to a terminal. NOT IMPLEMENTED YET + The first line has {row} zero. When {row} is invalid an empty + string is returned. -term_setsize() *term_setsize()* - Set the size of a terminal. NOT IMPLEMENTED YET +term_getsize({buf}) *term_getsize()* + Get the size of terminal {buf}. Returns a list with two + numbers: [rows, cols]. This is the size of the terminal, not + the window containing the terminal. -term_wait() *term_wait()* - Wait for screen to be updated. NOT IMPLEMENTED YET + {buf} must be the buffer number of a terminal window. If the + buffer does not exist or is not a terminal window, an empty + list is returned. + +term_list(}) *term_list()* + Return a list with the buffer numbers of all buffers for + terminal windows. + +term_scrape({buf}, {row}) *term_scrape()* + Get the contents of {row} of terminal screen of {buf}. + For {buf} see |term_getsize()|. + + The first {row} is zero. When {row} is invalid an empty list + is returned. + + Return a List containing a Dict for each screen cell: + "chars" character(s) at the cell + "fg" foreground color as #rrggbb + "bg" background color as #rrggbb + "attr" attributes of the cell, use term_getattr() + to get the individual flags + "width" cell width: 1 or 2 + +term_sendkeys({buf}, {keys}) *term_sendkeys()* + Send keystrokes {keys} to terminal {buf}. + {buf} is used as with |term_getsize()|. + + {keys} are translated as key sequences. For example, "\" + means the character CTRL-X. + +term_start({cmd}, {options}) *term_start()* + Open a terminal window and run {cmd} in it. + + Returns the buffer number of the terminal window. + When opening the window fails zero is returned. + + {options} are not implemented yet. + +term_wait({buf}) *term_wait()* + Wait for pending updates of {buf} to be handled. + {buf} is used as with |term_getsize()|. test_alloc_fail({id}, {countdown}, {repeat}) *test_alloc_fail()* This is for testing: If the memory allocation with {id} is diff --git a/src/Makefile b/src/Makefile index e522c78c48..e4ccf37e1e 100644 --- a/src/Makefile +++ b/src/Makefile @@ -2256,6 +2256,7 @@ test_arglist \ test_tagjump \ test_taglist \ test_tcl \ + test_terminal \ test_textobjects \ test_timers \ test_true_false \ diff --git a/src/evalfunc.c b/src/evalfunc.c index 30006e3e24..422b94e995 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -830,6 +830,17 @@ static struct fst {"tanh", 1, 1, f_tanh}, #endif {"tempname", 0, 0, f_tempname}, +#ifdef FEAT_TERMINAL + {"term_getattr", 2, 2, f_term_getattr}, + {"term_getjob", 1, 1, f_term_getjob}, + {"term_getline", 2, 2, f_term_getline}, + {"term_getsize", 1, 1, f_term_getsize}, + {"term_list", 0, 0, f_term_list}, + {"term_scrape", 2, 2, f_term_scrape}, + {"term_sendkeys", 2, 2, f_term_sendkeys}, + {"term_start", 1, 2, f_term_start}, + {"term_wait", 1, 1, f_term_wait}, +#endif {"test_alloc_fail", 3, 3, f_test_alloc_fail}, {"test_autochdir", 0, 0, f_test_autochdir}, {"test_garbagecollect_now", 0, 0, f_test_garbagecollect_now}, @@ -1540,7 +1551,7 @@ buflist_find_by_name(char_u *name, int curtab_only) /* * Get buffer by number or pattern. */ - static buf_T * + buf_T * get_buf_tv(typval_T *tv, int curtab_only) { char_u *name = tv->vval.v_string; diff --git a/src/proto/evalfunc.pro b/src/proto/evalfunc.pro index c673e27a53..174afb6f23 100644 --- a/src/proto/evalfunc.pro +++ b/src/proto/evalfunc.pro @@ -1,4 +1,5 @@ /* evalfunc.c */ +buf_T* get_buf_tv(typval_T *tv, int curtab_only); char_u *get_function_name(expand_T *xp, int idx); char_u *get_expr_name(expand_T *xp, int idx); int find_internal_func(char_u *name); diff --git a/src/proto/terminal.pro b/src/proto/terminal.pro index fbc0d1e810..daa7706485 100644 --- a/src/proto/terminal.pro +++ b/src/proto/terminal.pro @@ -3,6 +3,7 @@ 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 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); @@ -10,4 +11,13 @@ 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); int set_ref_in_term(int copyID); +void f_term_getattr(typval_T *argvars, typval_T *rettv); +void f_term_getjob(typval_T *argvars, typval_T *rettv); +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 : */ diff --git a/src/terminal.c b/src/terminal.c index 3972bcc833..fee87d5892 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -54,14 +54,6 @@ * - support minimal size when 'termsize' is empty? * - implement "term" for job_start(): more job options when starting a * terminal. - * - implement term_list() list of buffers with a terminal - * - implement term_getsize(buf) - * - implement term_setsize(buf) - * - implement term_sendkeys(buf, keys) send keystrokes to a terminal - * - implement term_wait(buf) wait for screen to be updated - * - implement term_scrape(buf, row) inspect terminal screen - * - implement term_open(command, options) open terminal window - * - implement term_getjob(buf) * - when 'encoding' is not utf-8, or the job is using another encoding, setup * conversions. * - In the GUI use a terminal emulator for :!cmd. @@ -69,7 +61,7 @@ #include "vim.h" -#ifdef FEAT_TERMINAL +#if defined(FEAT_TERMINAL) || defined(PROTO) #ifdef WIN3264 # define MIN(x,y) (x < y ? x : y) @@ -110,6 +102,7 @@ struct terminal_S { int tl_dirty_row_end; /* row below last one to update */ garray_T tl_scrollback; + int tl_scrollback_scrolled; pos_T tl_cursor; int tl_cursor_visible; @@ -384,9 +377,9 @@ write_to_term(buf_T *buffer, char_u *msg, channel_T *channel) * Return the number of bytes in "buf". */ static int -term_convert_key(int c, char *buf) +term_convert_key(term_T *term, int c, char *buf) { - VTerm *vterm = curbuf->b_term->tl_vterm; + VTerm *vterm = term->tl_vterm; VTermKey key = VTERM_KEY_NONE; VTermModifier mod = VTERM_MOD_NONE; @@ -516,6 +509,76 @@ term_vgetc() return c; } +/* + * Send keys to terminal. + */ + static int +send_keys_to_term(term_T *term, int c, int typed) +{ + char msg[KEY_BUF_LEN]; + size_t len; + static int mouse_was_outside = FALSE; + int dragging_outside = FALSE; + + /* Catch keys that need to be handled as in Normal mode. */ + switch (c) + { + case NUL: + case K_ZERO: + if (typed) + stuffcharReadbuff(c); + return FAIL; + + case K_IGNORE: + return FAIL; + + case K_LEFTDRAG: + case K_MIDDLEDRAG: + case K_RIGHTDRAG: + case K_X1DRAG: + case K_X2DRAG: + dragging_outside = mouse_was_outside; + /* FALLTHROUGH */ + case K_LEFTMOUSE: + case K_LEFTMOUSE_NM: + case K_LEFTRELEASE: + case K_LEFTRELEASE_NM: + case K_MIDDLEMOUSE: + case K_MIDDLERELEASE: + case K_RIGHTMOUSE: + case K_RIGHTRELEASE: + case K_X1MOUSE: + case K_X1RELEASE: + case K_X2MOUSE: + case K_X2RELEASE: + if (mouse_row < W_WINROW(curwin) + || mouse_row >= (W_WINROW(curwin) + curwin->w_height) + || mouse_col < W_WINCOL(curwin) + || mouse_col >= W_ENDCOL(curwin) + || dragging_outside) + { + /* click outside the current window */ + if (typed) + { + stuffcharReadbuff(c); + mouse_was_outside = TRUE; + } + return FAIL; + } + } + if (typed) + mouse_was_outside = FALSE; + + /* Convert the typed key to a sequence of bytes for the job. */ + len = term_convert_key(term, c, msg); + if (len > 0) + /* TODO: if FAIL is returned, stop? */ + channel_send(term->tl_job->jv_channel, PART_IN, + (char_u *)msg, (int)len, NULL); + + return OK; +} + /* * Wait for input and send it to the job. * Return when the start of a CTRL-W command is typed or anything else that @@ -526,11 +589,7 @@ term_vgetc() int terminal_loop(void) { - char buf[KEY_BUF_LEN]; int c; - size_t len; - static int mouse_was_outside = FALSE; - int dragging_outside = FALSE; int termkey = 0; if (curbuf->b_term->tl_vterm == NULL || !term_job_running(curbuf->b_term)) @@ -576,60 +635,41 @@ terminal_loop(void) return OK; } } - - /* Catch keys that need to be handled as in Normal mode. */ - switch (c) - { - case NUL: - case K_ZERO: - stuffcharReadbuff(c); - return OK; - - case K_IGNORE: continue; - - case K_LEFTDRAG: - case K_MIDDLEDRAG: - case K_RIGHTDRAG: - case K_X1DRAG: - case K_X2DRAG: - dragging_outside = mouse_was_outside; - /* FALLTHROUGH */ - case K_LEFTMOUSE: - case K_LEFTMOUSE_NM: - case K_LEFTRELEASE: - case K_LEFTRELEASE_NM: - case K_MIDDLEMOUSE: - case K_MIDDLERELEASE: - case K_RIGHTMOUSE: - case K_RIGHTRELEASE: - case K_X1MOUSE: - case K_X1RELEASE: - case K_X2MOUSE: - case K_X2RELEASE: - if (mouse_row < W_WINROW(curwin) - || mouse_row >= (W_WINROW(curwin) + curwin->w_height) - || mouse_col < W_WINCOL(curwin) - || mouse_col >= W_ENDCOL(curwin) - || dragging_outside) - { - /* click outside the current window */ - stuffcharReadbuff(c); - mouse_was_outside = TRUE; - return OK; - } - } - mouse_was_outside = FALSE; - - /* Convert the typed key to a sequence of bytes for the job. */ - len = term_convert_key(c, buf); - if (len > 0) - /* TODO: if FAIL is returned, stop? */ - channel_send(curbuf->b_term->tl_job->jv_channel, PART_IN, - (char_u *)buf, (int)len, NULL); + if (send_keys_to_term(curbuf->b_term, c, TRUE) != OK) + return OK; } return FAIL; } +/* + * Called when a job has finished. + */ + 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(); + if (curbuf->b_term != NULL) + { + if (curbuf->b_term->tl_job == job) + maketitle(); + update_cursor(curbuf->b_term, TRUE); + } +} + static void position_cursor(win_T *wp, VTermPos *pos) { @@ -789,6 +829,7 @@ handle_pushline(int cols, const VTermScreenCell *cells, void *user) line->sb_cols = len; line->sb_cells = p; ++term->tl_scrollback.ga_len; + ++term->tl_scrollback_scrolled; } return 0; /* ignored */ } @@ -916,6 +957,7 @@ static VTermScreenCallbacks screen_callbacks = { /* * Called when a channel has been closed. + * If this was a channel for a terminal window then finish it up. */ void term_channel_closed(channel_T *ch) @@ -1080,8 +1122,6 @@ cell2attr(VTermScreenCell *cell) attr |= HL_STANDOUT; if (cell->attrs.reverse) attr |= HL_INVERSE; - if (cell->attrs.strike) - attr |= HL_UNDERLINE; #ifdef FEAT_GUI if (gui.in_use) @@ -1384,8 +1424,315 @@ set_ref_in_term(int copyID) return abort; } +/* + * "term_getattr(attr, name)" function + */ + void +f_term_getattr(typval_T *argvars, typval_T *rettv) +{ + int attr; + size_t i; + char_u *name; + + static struct { + char *name; + int attr; + } attrs[] = { + {"bold", HL_BOLD}, + {"italic", HL_ITALIC}, + {"underline", HL_UNDERLINE}, + {"strike", HL_STANDOUT}, + {"reverse", HL_INVERSE}, + }; + + attr = get_tv_number(&argvars[0]); + name = get_tv_string_chk(&argvars[1]); + if (name == NULL) + return; + + for (i = 0; i < sizeof(attrs)/sizeof(attrs[0]); ++i) + if (STRCMP(name, attrs[i].name) == 0) + { + rettv->vval.v_number = (attr & attrs[i].attr) != 0 ? 1 : 0; + break; + } +} + +/* + * Get the buffer from the first argument in "argvars". + * Returns NULL when the buffer is not for a terminal window. + */ + static buf_T * +term_get_buf(typval_T *argvars) +{ + buf_T *buf; + + (void)get_tv_number(&argvars[0]); /* issue errmsg if type error */ + ++emsg_off; + buf = get_buf_tv(&argvars[0], FALSE); + --emsg_off; + if (buf->b_term == NULL) + return NULL; + return buf; +} + +/* + * "term_getjob(buf)" function + */ + void +f_term_getjob(typval_T *argvars, typval_T *rettv) +{ + buf_T *buf = term_get_buf(argvars); + + rettv->v_type = VAR_JOB; + rettv->vval.v_job = NULL; + if (buf == NULL) + return; + + rettv->vval.v_job = buf->b_term->tl_job; + if (rettv->vval.v_job != NULL) + ++rettv->vval.v_job->jv_refcount; +} + +/* + * "term_getline(buf, row)" function + */ + void +f_term_getline(typval_T *argvars, typval_T *rettv) +{ + buf_T *buf = term_get_buf(argvars); + term_T *term; + int row; + + rettv->v_type = VAR_STRING; + if (buf == NULL) + return; + term = buf->b_term; + row = (int)get_tv_number(&argvars[1]); + + if (term->tl_vterm == NULL) + { + linenr_T lnum = row + term->tl_scrollback_scrolled + 1; + + /* vterm is finished, get the text from the buffer */ + if (lnum > 0 && lnum <= buf->b_ml.ml_line_count) + rettv->vval.v_string = vim_strsave(ml_get_buf(buf, lnum, FALSE)); + } + else + { + VTermScreen *screen = vterm_obtain_screen(term->tl_vterm); + VTermRect rect; + int len; + char_u *p; + + len = term->tl_cols * MB_MAXBYTES + 1; + p = alloc(len); + if (p == NULL) + return; + rettv->vval.v_string = p; + + rect.start_col = 0; + rect.end_col = term->tl_cols; + rect.start_row = row; + rect.end_row = row + 1; + p[vterm_screen_get_text(screen, (char *)p, len, rect)] = NUL; + } +} + +/* + * "term_getsize(buf)" function + */ + void +f_term_getsize(typval_T *argvars, typval_T *rettv) +{ + buf_T *buf = term_get_buf(argvars); + list_T *l; + + if (rettv_list_alloc(rettv) == FAIL) + return; + if (buf == NULL) + return; + + l = rettv->vval.v_list; + list_append_number(l, buf->b_term->tl_rows); + list_append_number(l, buf->b_term->tl_cols); +} + +/* + * "term_list()" function + */ + void +f_term_list(typval_T *argvars UNUSED, typval_T *rettv) +{ + term_T *tp; + list_T *l; + + if (rettv_list_alloc(rettv) == FAIL || first_term == NULL) + return; + + l = rettv->vval.v_list; + for (tp = first_term; tp != NULL; tp = tp->tl_next) + if (tp != NULL && tp->tl_buffer != NULL) + if (list_append_number(l, + (varnumber_T)tp->tl_buffer->b_fnum) == FAIL) + return; +} + +/* + * "term_scrape(buf, row)" function + */ + void +f_term_scrape(typval_T *argvars, typval_T *rettv) +{ + buf_T *buf = term_get_buf(argvars); + VTermScreen *screen = NULL; + VTermPos pos; + list_T *l; + term_T *term; + + if (rettv_list_alloc(rettv) == FAIL) + return; + if (buf == NULL) + return; + term = buf->b_term; + if (term->tl_vterm != NULL) + screen = vterm_obtain_screen(term->tl_vterm); + + l = rettv->vval.v_list; + pos.row = (int)get_tv_number(&argvars[1]); + for (pos.col = 0; pos.col < term->tl_cols; ) + { + dict_T *dcell; + VTermScreenCell cell; + char_u rgb[8]; + char_u mbs[MB_MAXBYTES * VTERM_MAX_CHARS_PER_CELL + 1]; + int off = 0; + int i; + + if (screen == NULL) + { + linenr_T lnum = pos.row + term->tl_scrollback_scrolled; + sb_line_T *line; + + /* vterm has finished, get the cell from scrollback */ + if (lnum < 0 || lnum >= term->tl_scrollback.ga_len) + break; + line = (sb_line_T *)term->tl_scrollback.ga_data + lnum; + if (pos.col >= line->sb_cols) + break; + cell = line->sb_cells[pos.col]; + } + else if (vterm_screen_get_cell(screen, pos, &cell) == 0) + break; + dcell = dict_alloc(); + list_append_dict(l, dcell); + + for (i = 0; i < VTERM_MAX_CHARS_PER_CELL; ++i) + { + if (cell.chars[i] == 0) + break; + off += (*utf_char2bytes)((int)cell.chars[i], mbs + off); + } + mbs[off] = NUL; + dict_add_nr_str(dcell, "chars", 0, mbs); + + vim_snprintf((char *)rgb, 8, "#%02x%02x%02x", + cell.fg.red, cell.fg.green, cell.fg.blue); + dict_add_nr_str(dcell, "fg", 0, rgb); + vim_snprintf((char *)rgb, 8, "#%02x%02x%02x", + cell.bg.red, cell.bg.green, cell.bg.blue); + dict_add_nr_str(dcell, "bg", 0, rgb); + + dict_add_nr_str(dcell, "attr", cell2attr(&cell), NULL); + dict_add_nr_str(dcell, "width", cell.width, NULL); + + ++pos.col; + if (cell.width == 2) + ++pos.col; + } +} + +/* + * "term_sendkeys(buf, keys)" function + */ + void +f_term_sendkeys(typval_T *argvars, typval_T *rettv) +{ + buf_T *buf = term_get_buf(argvars); + char_u *msg; + term_T *term; + + rettv->v_type = VAR_UNKNOWN; + if (buf == NULL) + return; + + msg = get_tv_string_chk(&argvars[1]); + if (msg == NULL) + return; + term = buf->b_term; + if (term->tl_vterm == NULL) + return; + + while (*msg != NUL) + { + send_keys_to_term(term, PTR2CHAR(msg), FALSE); + msg += MB_PTR2LEN(msg); + } + + /* TODO: only update once in a while. */ + update_screen(0); + if (buf == curbuf) + update_cursor(term, TRUE); +} + +/* + * "term_start(command, options)" function + */ + void +f_term_start(typval_T *argvars, typval_T *rettv) +{ + char_u *cmd = get_tv_string_chk(&argvars[0]); + exarg_T ea; + + if (cmd == NULL) + return; + ea.arg = cmd; + ex_terminal(&ea); + + if (curbuf->b_term != NULL) + rettv->vval.v_number = curbuf->b_fnum; +} + +/* + * "term_wait" function + */ + void +f_term_wait(typval_T *argvars, typval_T *rettv UNUSED) +{ + buf_T *buf = term_get_buf(argvars); + + if (buf == NULL) + return; + + /* Get the job status, this will detect a job that finished. */ + if (buf->b_term->tl_job != NULL) + (void)job_status(buf->b_term->tl_job); + + /* Check for any pending channel I/O. */ + vpeekc_any(); + ui_delay(10L, FALSE); + + /* Flushing messages on channels is hopefully sufficient. + * TODO: is there a better way? */ + parse_queued_messages(); +} + # ifdef WIN3264 +/************************************** + * 2. MS-Windows implementation. + */ + #define WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN 1ul #define WINPTY_SPAWN_FLAG_EXIT_AFTER_SHUTDOWN 2ull @@ -1404,10 +1751,6 @@ void (*winpty_error_free)(void*); LPCWSTR (*winpty_error_msg)(void*); BOOL (*winpty_set_size)(void*, int, int, void*); -/************************************** - * 2. MS-Windows implementation. - */ - #define WINPTY_DLL "winpty.dll" static HINSTANCE hWinPtyDLL = NULL; diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak index 1f8d307290..4eb634c58f 100644 --- a/src/testdir/Make_all.mak +++ b/src/testdir/Make_all.mak @@ -197,6 +197,7 @@ NEW_TESTS = test_arabic.res \ test_syntax.res \ test_system.res \ test_tcl.res \ + test_terminal.res \ test_textobjects.res \ test_undo.res \ test_usercommands.res \ diff --git a/src/testdir/test_terminal.vim b/src/testdir/test_terminal.vim new file mode 100644 index 0000000000..10fe4af612 --- /dev/null +++ b/src/testdir/test_terminal.vim @@ -0,0 +1,67 @@ +" Tests for the terminal window. + +if !exists('*term_start') + finish +endif + +source shared.vim + +func Test_terminal_basic() + let buf = term_start(&shell) + + let termlist = term_list() + call assert_equal(1, len(termlist)) + call assert_equal(buf, termlist[0]) + + let g:job = term_getjob(buf) + call assert_equal(v:t_job, type(g:job)) + + call term_sendkeys(buf, "exit\r") + call WaitFor('job_status(g:job) == "dead"') + call assert_equal('dead', job_status(g:job)) + + exe buf . 'bwipe' + unlet g:job +endfunc + +func Check_123(buf) + let l = term_scrape(a:buf, 0) + call assert_true(len(l) > 0) + call assert_equal('1', l[0].chars) + call assert_equal('2', l[1].chars) + call assert_equal('3', l[2].chars) + call assert_equal('#00e000', l[0].fg) + if &background == 'light' + call assert_equal('#ffffff', l[0].bg) + else + call assert_equal('#000000', l[0].bg) + endif + + let l = term_getline(a:buf, 0) + call assert_equal('123', l) +endfunc + +func Test_terminal_scrape() + if has('win32') + let cmd = 'cmd /c "cls && color 2 && echo 123"' + else + call writefile(["\[32m123"], 'Xtext') + let cmd = "cat Xtext" + endif + let buf = term_start(cmd) + + let termlist = term_list() + call assert_equal(1, len(termlist)) + call assert_equal(buf, termlist[0]) + + call term_wait(buf) + call Check_123(buf) + + " Must still work after the job ended. + let g:job = term_getjob(buf) + call WaitFor('job_status(g:job) == "dead"') + call term_wait(buf) + call Check_123(buf) + + exe buf . 'bwipe' +endfunc diff --git a/src/version.c b/src/version.c index 44ae124a4f..86e8cc6d68 100644 --- a/src/version.c +++ b/src/version.c @@ -769,6 +769,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 803, /**/ 802, /**/ From 91d348abfc0d7ac9b5b19cde0112de8ae4335602 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 29 Jul 2017 20:16:03 +0200 Subject: [PATCH 05/24] patch 8.0.0804: running tests fails when stdin is /dev/null Problem: Running tests fails when stdin is /dev/null. (James McCoy) Solution: Do not bail out from getting input if the --not-a-term argument was given. (closes #1460) --- src/eval.c | 5 +++-- src/evalfunc.c | 5 +++-- src/version.c | 2 ++ 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/eval.c b/src/eval.c index 4cb32893f1..c35def0b27 100644 --- a/src/eval.c +++ b/src/eval.c @@ -8070,8 +8070,9 @@ get_user_input( rettv->vval.v_string = NULL; #ifdef NO_CONSOLE_INPUT - /* While starting up, there is no place to enter text. */ - if (no_console_input()) + /* While starting up, there is no place to enter text. When running tests + * with --not-a-term we assume feedkeys() will be used. */ + if (no_console_input() && !is_not_a_term()) return; #endif diff --git a/src/evalfunc.c b/src/evalfunc.c index 422b94e995..7a2e4aad59 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -6482,8 +6482,9 @@ f_inputlist(typval_T *argvars, typval_T *rettv) int mouse_used; #ifdef NO_CONSOLE_INPUT - /* While starting up, there is no place to enter text. */ - if (no_console_input()) + /* While starting up, there is no place to enter text. When running tests + * with --not-a-term we assume feedkeys() will be used. */ + if (no_console_input() && !is_not_a_term()) return; #endif if (argvars[0].v_type != VAR_LIST || argvars[0].vval.v_list == NULL) diff --git a/src/version.c b/src/version.c index 86e8cc6d68..0648dd6ec7 100644 --- a/src/version.c +++ b/src/version.c @@ -769,6 +769,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 804, /**/ 803, /**/ From d1ee0043c0360f6b504c0283edd2a844ef73a349 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 29 Jul 2017 20:39:53 +0200 Subject: [PATCH 06/24] patch 8.0.0805: GUI test fails with gnome2 Problem: GUI test fails with gnome2. Solution: Set $HOME to an existing directory. --- src/testdir/runtest.vim | 3 +++ src/testdir/setup.vim | 4 +++- src/version.c | 2 ++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/testdir/runtest.vim b/src/testdir/runtest.vim index 0256d4899c..c57ec5f642 100644 --- a/src/testdir/runtest.vim +++ b/src/testdir/runtest.vim @@ -158,6 +158,9 @@ func FinishTesting() " Don't write viminfo on exit. set viminfo= + " Clean up files created by setup.vim + call delete('XfakeHOME', 'rf') + if s:fail == 0 " Success, create the .res file so that make knows it's done. exe 'split ' . fnamemodify(g:testname, ':r') . '.res' diff --git a/src/testdir/setup.vim b/src/testdir/setup.vim index 4b6c089637..6299852ea4 100644 --- a/src/testdir/setup.vim +++ b/src/testdir/setup.vim @@ -14,5 +14,7 @@ if 1 endif " Make sure $HOME does not get read or written. - let $HOME = '/does/not/exist' + " It must exist, gnome tries to create $HOME/.gnome2 + let $HOME = getcwd() . '/XfakeHOME' + call mkdir($HOME) endif diff --git a/src/version.c b/src/version.c index 0648dd6ec7..ed7880c856 100644 --- a/src/version.c +++ b/src/version.c @@ -769,6 +769,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 805, /**/ 804, /**/ From f98246d4849e670c6660e97887428dcddf88dc9f Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 29 Jul 2017 22:21:18 +0200 Subject: [PATCH 07/24] patch 8.0.0806: tests may try to create XfakeHOME twice Problem: Tests may try to create XfakeHOME twice. Solution: Avoid loading setup.vim twice. --- src/testdir/setup.vim | 8 ++++++++ src/version.c | 2 ++ 2 files changed, 10 insertions(+) diff --git a/src/testdir/setup.vim b/src/testdir/setup.vim index 6299852ea4..31b133f10d 100644 --- a/src/testdir/setup.vim +++ b/src/testdir/setup.vim @@ -1,5 +1,13 @@ " Common preparations for running tests. +" Only load this once. +if 1 + if exists('s:did_load') + finish + endif + let s:did_load = 1 +endif + " Make sure 'runtimepath' and 'packpath' does not include $HOME. set rtp=$VIM/vimfiles,$VIMRUNTIME,$VIM/vimfiles/after if has('packages') diff --git a/src/version.c b/src/version.c index ed7880c856..cde101ab63 100644 --- a/src/version.c +++ b/src/version.c @@ -769,6 +769,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 806, /**/ 805, /**/ From 6e1ef28057b100b206a88a175b37e75f5130668c Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 29 Jul 2017 22:23:40 +0200 Subject: [PATCH 08/24] patch 8.0.0807: terminal window can't handle mouse buttons Problem: Terminal window can't handle mouse buttons. (Hirohito Higashi) Solution: Implement mouse buttons and many other keys. Ignore the ones that are not implemented. --- src/terminal.c | 111 ++++++++++++++++++++++++++++++++++++++----------- src/version.c | 2 + 2 files changed, 88 insertions(+), 25 deletions(-) diff --git a/src/terminal.c b/src/terminal.c index fee87d5892..1863b59861 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -36,7 +36,6 @@ * that buffer, attributes come from the scrollback buffer tl_scrollback. * * TODO: - * - Patch for functions: Yasuhiro Matsumoto, #1871 * - For the scrollback buffer store lines in the buffer, only attributes in * tl_scrollback. * - When the job ends: @@ -372,6 +371,20 @@ write_to_term(buf_T *buffer, char_u *msg, channel_T *channel) update_cursor(term, TRUE); } +/* + * Send a mouse position and click to the vterm + */ + static int +term_send_mouse(VTerm *vterm, int button, int pressed) +{ + VTermModifier mod = VTERM_MOD_NONE; + + vterm_mouse_move(vterm, mouse_row - W_WINROW(curwin), + mouse_col - W_WINCOL(curwin), mod); + vterm_mouse_button(vterm, button, pressed, mod); + return TRUE; +} + /* * Convert typed key "c" into bytes to send to the job. * Return the number of bytes in "buf". @@ -382,6 +395,7 @@ term_convert_key(term_T *term, int c, char *buf) VTerm *vterm = term->tl_vterm; VTermKey key = VTERM_KEY_NONE; VTermModifier mod = VTERM_MOD_NONE; + int mouse = FALSE; switch (c) { @@ -391,7 +405,13 @@ term_convert_key(term_T *term, int c, char *buf) case K_BS: c = BS; break; case K_DEL: key = VTERM_KEY_DEL; break; case K_DOWN: key = VTERM_KEY_DOWN; break; + case K_S_DOWN: mod = VTERM_MOD_SHIFT; + key = VTERM_KEY_DOWN; break; case K_END: key = VTERM_KEY_END; break; + case K_S_END: mod = VTERM_MOD_SHIFT; + key = VTERM_KEY_END; break; + case K_C_END: mod = VTERM_MOD_CTRL; + key = VTERM_KEY_END; break; case K_F10: key = VTERM_KEY_FUNCTION(10); break; case K_F11: key = VTERM_KEY_FUNCTION(11); break; case K_F12: key = VTERM_KEY_FUNCTION(12); break; @@ -405,6 +425,10 @@ term_convert_key(term_T *term, int c, char *buf) case K_F8: key = VTERM_KEY_FUNCTION(8); break; case K_F9: key = VTERM_KEY_FUNCTION(9); break; case K_HOME: key = VTERM_KEY_HOME; break; + case K_S_HOME: mod = VTERM_MOD_SHIFT; + key = VTERM_KEY_HOME; break; + case K_C_HOME: mod = VTERM_MOD_CTRL; + key = VTERM_KEY_HOME; break; case K_INS: key = VTERM_KEY_INS; break; case K_K0: key = VTERM_KEY_KP_0; break; case K_K1: key = VTERM_KEY_KP_1; break; @@ -429,48 +453,85 @@ term_convert_key(term_T *term, int c, char *buf) case K_KPLUS: key = VTERM_KEY_KP_PLUS; break; case K_KPOINT: key = VTERM_KEY_KP_PERIOD; break; case K_LEFT: key = VTERM_KEY_LEFT; break; + case K_S_LEFT: mod = VTERM_MOD_SHIFT; + key = VTERM_KEY_LEFT; break; + case K_C_LEFT: mod = VTERM_MOD_CTRL; + key = VTERM_KEY_LEFT; break; case K_PAGEDOWN: key = VTERM_KEY_PAGEDOWN; break; case K_PAGEUP: key = VTERM_KEY_PAGEUP; break; case K_RIGHT: key = VTERM_KEY_RIGHT; break; + case K_S_RIGHT: mod = VTERM_MOD_SHIFT; + key = VTERM_KEY_RIGHT; break; + case K_C_RIGHT: mod = VTERM_MOD_CTRL; + key = VTERM_KEY_RIGHT; break; case K_UP: key = VTERM_KEY_UP; break; + case K_S_UP: mod = VTERM_MOD_SHIFT; + key = VTERM_KEY_UP; break; case TAB: key = VTERM_KEY_TAB; break; - case K_MOUSEUP: /* TODO */ break; - case K_MOUSEDOWN: /* TODO */ break; - case K_MOUSELEFT: /* TODO */ break; - case K_MOUSERIGHT: /* TODO */ break; + case K_MOUSEUP: mouse = term_send_mouse(vterm, 5, 1); break; + case K_MOUSEDOWN: mouse = term_send_mouse(vterm, 4, 1); break; + case K_MOUSELEFT: /* TODO */ return 0; + case K_MOUSERIGHT: /* TODO */ return 0; - case K_LEFTMOUSE: /* TODO */ break; - case K_LEFTMOUSE_NM: /* TODO */ break; - case K_LEFTDRAG: /* TODO */ break; - case K_LEFTRELEASE: /* TODO */ break; - case K_LEFTRELEASE_NM: /* TODO */ break; - case K_MIDDLEMOUSE: /* TODO */ break; - case K_MIDDLEDRAG: /* TODO */ break; - case K_MIDDLERELEASE: /* TODO */ break; - case K_RIGHTMOUSE: /* TODO */ break; - case K_RIGHTDRAG: /* TODO */ break; - case K_RIGHTRELEASE: /* TODO */ break; - case K_X1MOUSE: /* TODO */ break; - case K_X1DRAG: /* TODO */ break; - case K_X1RELEASE: /* TODO */ break; - case K_X2MOUSE: /* TODO */ break; - case K_X2DRAG: /* TODO */ break; - case K_X2RELEASE: /* TODO */ break; + case K_LEFTMOUSE: + case K_LEFTMOUSE_NM: mouse = term_send_mouse(vterm, 1, 1); break; + case K_LEFTDRAG: mouse = term_send_mouse(vterm, 1, 1); break; + case K_LEFTRELEASE: + case K_LEFTRELEASE_NM: mouse = term_send_mouse(vterm, 1, 0); break; + case K_MIDDLEMOUSE: mouse = term_send_mouse(vterm, 2, 1); break; + case K_MIDDLEDRAG: mouse = term_send_mouse(vterm, 2, 1); break; + case K_MIDDLERELEASE: mouse = term_send_mouse(vterm, 2, 0); break; + case K_RIGHTMOUSE: mouse = term_send_mouse(vterm, 3, 1); break; + case K_RIGHTDRAG: mouse = term_send_mouse(vterm, 3, 1); break; + case K_RIGHTRELEASE: mouse = term_send_mouse(vterm, 3, 0); break; + case K_X1MOUSE: /* TODO */ return 0; + case K_X1DRAG: /* TODO */ return 0; + case K_X1RELEASE: /* TODO */ return 0; + case K_X2MOUSE: /* TODO */ return 0; + case K_X2DRAG: /* TODO */ return 0; + case K_X2RELEASE: /* TODO */ return 0; - /* TODO: handle all special keys and modifiers that terminal_loop() - * does not handle. */ + case K_IGNORE: return 0; + case K_NOP: return 0; + case K_UNDO: return 0; + case K_HELP: return 0; + case K_XF1: key = VTERM_KEY_FUNCTION(1); break; + case K_XF2: key = VTERM_KEY_FUNCTION(2); break; + case K_XF3: key = VTERM_KEY_FUNCTION(3); break; + case K_XF4: key = VTERM_KEY_FUNCTION(4); break; + case K_SELECT: return 0; +#ifdef FEAT_GUI + case K_VER_SCROLLBAR: return 0; + case K_HOR_SCROLLBAR: return 0; +#endif +#ifdef FEAT_GUI_TABLINE + case K_TABLINE: return 0; + case K_TABMENU: return 0; +#endif +#ifdef FEAT_NETBEANS_INTG + case K_F21: key = VTERM_KEY_FUNCTION(21); break; +#endif +#ifdef FEAT_DND + case K_DROP: return 0; +#endif +#ifdef FEAT_AUTOCMD + case K_CURSORHOLD: return 0; +#endif + case K_PS: vterm_keyboard_start_paste(vterm); return 0; + case K_PE: vterm_keyboard_end_paste(vterm); return 0; } /* * Convert special keys to vterm keys: * - Write keys to vterm: vterm_keyboard_key() * - Write output to channel. + * TODO: use mod_mask */ if (key != VTERM_KEY_NONE) /* Special key, let vterm convert it. */ vterm_keyboard_key(vterm, key, mod); - else + else if (!mouse) /* Normal character, let vterm convert it. */ vterm_keyboard_unichar(vterm, c, mod); diff --git a/src/version.c b/src/version.c index cde101ab63..95468b2ffb 100644 --- a/src/version.c +++ b/src/version.c @@ -769,6 +769,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 807, /**/ 806, /**/ From d507a685ad9cd270e5afc3bd4aa6a80ddb0d8802 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 29 Jul 2017 22:41:18 +0200 Subject: [PATCH 09/24] patch 8.0.0808: cannot build with terminal feature and DEBUG defined Problem: Cannot build with terminal feature and DEBUG defined. (Christian Brabandt) Solution: Use DEBUG_LOG3(). --- src/libvterm/src/pen.c | 2 +- src/version.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libvterm/src/pen.c b/src/libvterm/src/pen.c index 3c58c9cec9..fd604334f5 100644 --- a/src/libvterm/src/pen.c +++ b/src/libvterm/src/pen.c @@ -106,7 +106,7 @@ static void setpenattr(VTermState *state, VTermAttr attr, VTermValueType type UN { #ifdef DEBUG if(type != vterm_get_attr_type(attr)) { - DEBUG_LOG("Cannot set attr %d as it has type %d, not type %d\n", + DEBUG_LOG3("Cannot set attr %d as it has type %d, not type %d\n", attr, vterm_get_attr_type(attr), type); return; } diff --git a/src/version.c b/src/version.c index 95468b2ffb..ab9f30eaf5 100644 --- a/src/version.c +++ b/src/version.c @@ -769,6 +769,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 808, /**/ 807, /**/ From 30fe88677dd2b16e733ca0ac14edc8d70c7259fc Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 29 Jul 2017 23:01:57 +0200 Subject: [PATCH 10/24] patch 8.0.0809: MS-Windows: tests hang Problem: MS-Windows: tests hang. Solution: Delete the XfakeHOME directory. --- src/testdir/Make_dos.mak | 2 ++ src/testdir/Make_ming.mak | 2 ++ src/version.c | 2 ++ 3 files changed, 6 insertions(+) diff --git a/src/testdir/Make_dos.mak b/src/testdir/Make_dos.mak index fe2e2ba25f..2c00a156f1 100644 --- a/src/testdir/Make_dos.mak +++ b/src/testdir/Make_dos.mak @@ -54,6 +54,7 @@ $(TEST_OUTFILES): $(DOSTMP)\$(*B).in -@if exist test.ok del test.ok -@if exist Xdir1 rd /s /q Xdir1 -@if exist Xfind rd /s /q Xfind + -@if exist XfakeHOME rd /s /q XfakeHOME -@del X* -@if exist viminfo del viminfo $(VIMPROG) -u dos.vim $(NO_INITS) "+set ff=unix|f test.out|wq" \ @@ -89,6 +90,7 @@ clean: -if exist lua.vim del lua.vim -if exist Xdir1 rd /s /q Xdir1 -if exist Xfind rd /s /q Xfind + -if exist XfakeHOME rd /s /q XfakeHOME -del X* -for /d %i in (X*) do @rmdir /s/q %i -if exist viminfo del viminfo diff --git a/src/testdir/Make_ming.mak b/src/testdir/Make_ming.mak index 31dfe702b7..3d7cf3fc65 100644 --- a/src/testdir/Make_ming.mak +++ b/src/testdir/Make_ming.mak @@ -84,6 +84,7 @@ clean: -@if exist lua.vim $(DEL) lua.vim -@if exist Xdir1 $(DELDIR) Xdir1 -@if exist Xfind $(DELDIR) Xfind + -@if exist XfakeHOME $(DELDIR) XfakeHOME -@if exist X* $(DEL) X* -@if exist viminfo $(DEL) viminfo -@if exist test.log $(DEL) test.log @@ -98,6 +99,7 @@ clean: @$(MV) test.out $*.out -@if exist Xdir1 $(DELDIR) Xdir1 -@if exist Xfind $(DELDIR) Xfind + -@if exist XfakeHOME $(DELDIR) XfakeHOME -@if exist X* $(DEL) X* -@if exist test.ok $(DEL) test.ok -@if exist viminfo $(DEL) viminfo diff --git a/src/version.c b/src/version.c index ab9f30eaf5..94aa40e763 100644 --- a/src/version.c +++ b/src/version.c @@ -769,6 +769,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 809, /**/ 808, /**/ From d0b6c6c54e0f3c2984e1d9b316a587ef14cfd53a Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 30 Jul 2017 13:28:23 +0200 Subject: [PATCH 11/24] patch 8.0.0810: MS-Windows: tests still hang Problem: MS-Windows: tests still hang. Solution: Only create the XfakeHOME directory if it does not exist yet. --- src/testdir/setup.vim | 4 +++- src/version.c | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/testdir/setup.vim b/src/testdir/setup.vim index 31b133f10d..cb8bebb47a 100644 --- a/src/testdir/setup.vim +++ b/src/testdir/setup.vim @@ -24,5 +24,7 @@ if 1 " Make sure $HOME does not get read or written. " It must exist, gnome tries to create $HOME/.gnome2 let $HOME = getcwd() . '/XfakeHOME' - call mkdir($HOME) + if !isdirectory($HOME) + call mkdir($HOME) + endif endif diff --git a/src/version.c b/src/version.c index 94aa40e763..fa67f7f570 100644 --- a/src/version.c +++ b/src/version.c @@ -769,6 +769,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 810, /**/ 809, /**/ From 5449f7c6fc574fed1020d70878becbbfca5a5a41 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 30 Jul 2017 13:51:37 +0200 Subject: [PATCH 12/24] patch 8.0.0811: MS-Windows: test_expand_dllpath fails Problem: MS-Windows: test_expand_dllpath fails. Solution: Change backslashes to forward slashes --- src/testdir/test_expand_dllpath.vim | 3 ++- src/version.c | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/testdir/test_expand_dllpath.vim b/src/testdir/test_expand_dllpath.vim index b3e099a450..1542095671 100644 --- a/src/testdir/test_expand_dllpath.vim +++ b/src/testdir/test_expand_dllpath.vim @@ -6,7 +6,8 @@ func s:test_expand_dllpath(optname) execute 'call assert_equal("' . $TEST_EXPAND_DLLPATH . '", &' . a:optname . ')' execute 'set ' . a:optname . '=~' . $TEST_EXPAND_DLLPATH - execute 'call assert_equal("' . $HOME . $TEST_EXPAND_DLLPATH . '", &' . a:optname . ')' + let home = substitute($HOME, '\\', '/', 'g') + execute 'call assert_equal("' . home . $TEST_EXPAND_DLLPATH . '", &' . a:optname . ')' finally execute 'let &' . a:optname . ' = dllpath_save' let $TEST_EXPAND_DLLPATH = '' diff --git a/src/version.c b/src/version.c index fa67f7f570..90d9273eba 100644 --- a/src/version.c +++ b/src/version.c @@ -769,6 +769,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 811, /**/ 810, /**/ From 68c4bdd53cb54b6c0dccb2dcee446ef42ef3dedd Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 30 Jul 2017 13:57:41 +0200 Subject: [PATCH 13/24] patch 8.0.0812: terminal window colors shift when 'number' is set Problem: Terminal window colors shift when 'number' is set. (Nazri Ramliy) Solution: Use vcol instead of col. --- src/screen.c | 2 +- src/version.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/screen.c b/src/screen.c index 40f83c0b6f..a1e8b5fa05 100644 --- a/src/screen.c +++ b/src/screen.c @@ -4541,7 +4541,7 @@ win_line( #ifdef FEAT_TERMINAL if (get_term_attr) { - syntax_attr = term_get_attr(wp->w_buffer, lnum, col); + syntax_attr = term_get_attr(wp->w_buffer, lnum, vcol); if (!attr_pri) char_attr = syntax_attr; diff --git a/src/version.c b/src/version.c index 90d9273eba..7a3e04a174 100644 --- a/src/version.c +++ b/src/version.c @@ -769,6 +769,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 812, /**/ 811, /**/ From 423802d1a282df35078539970eabf559186e1ec8 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 30 Jul 2017 16:52:24 +0200 Subject: [PATCH 14/24] patch 8.0.0813: cannot use a terminal window while the job is running Problem: Cannot use Vim commands in a terminal window while the job is running. Solution: Implement Terminal Normal mode. --- runtime/doc/terminal.txt | 46 ++++- src/main.c | 14 +- src/normal.c | 8 + src/option.c | 12 +- src/proto/terminal.pro | 6 +- src/screen.c | 2 +- src/terminal.c | 398 +++++++++++++++++++++++++++------------ src/version.c | 2 + 8 files changed, 351 insertions(+), 137 deletions(-) diff --git a/runtime/doc/terminal.txt b/runtime/doc/terminal.txt index 89689463ac..1054037838 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 28 +*terminal.txt* For Vim version 8.0. Last change: 2017 Jul 30 VIM REFERENCE MANUAL by Bram Moolenaar @@ -33,24 +33,39 @@ Or to run a debugger: > 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. +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 -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* @@ -99,6 +114,25 @@ terminal. |term_setsize()| can be used only when in the first or second mode, 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 diff --git a/src/main.c b/src/main.c index 268c256270..81eb1a6799 100644 --- a/src/main.c +++ b/src/main.c @@ -1356,11 +1356,17 @@ main_loop( else { #ifdef FEAT_TERMINAL - if (curbuf->b_term != NULL && oa.op_type == OP_NOP - && oa.regname == NUL) - terminal_loop(); + 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); + normal_cmd(&oa, TRUE); } } } diff --git a/src/normal.c b/src/normal.c index c8e78410d9..e8f80f181b 100644 --- a/src/normal.c +++ b/src/normal.c @@ -9037,6 +9037,14 @@ nv_esc(cmdarg_T *cap) 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'; diff --git a/src/option.c b/src/option.c index ad5892581d..6e4d1a5df6 100644 --- a/src/option.c +++ b/src/option.c @@ -8222,12 +8222,22 @@ set_bool_option( } #endif -#ifdef FEAT_TITLE /* 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) { diff --git a/src/proto/terminal.pro b/src/proto/terminal.pro index daa7706485..6f5b3ab707 100644 --- a/src/proto/terminal.pro +++ b/src/proto/terminal.pro @@ -2,11 +2,15 @@ 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,8 +20,8 @@ void f_term_getjob(typval_T *argvars, typval_T *rettv); 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_start(typval_T *argvars, typval_T *rettv); void f_term_wait(typval_T *argvars, typval_T *rettv); /* vim: set ft=c : */ diff --git a/src/screen.c b/src/screen.c index a1e8b5fa05..baedea755b 100644 --- a/src/screen.c +++ b/src/screen.c @@ -3245,7 +3245,7 @@ win_line( #endif #ifdef FEAT_TERMINAL - if (term_is_finished(wp->w_buffer)) + if (term_show_buffer(wp->w_buffer)) { extra_check = TRUE; get_term_attr = TRUE; diff --git a/src/terminal.c b/src/terminal.c index 1863b59861..b0ba8c1902 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -36,13 +36,23 @@ * 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. + * 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 buffer in viminfo. Or prefix term:// ? + * - 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,6 +63,8 @@ * - 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,13 +90,17 @@ typedef struct sb_line_S { 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 - VTerm *tl_vterm; - job_T *tl_job; - buf_T *tl_buffer; /* last known vterm size */ int tl_rows; @@ -552,6 +568,205 @@ term_job_running(term_T *term) && channel_is_open(term->tl_job->jv_channel); } +/* + * 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. @@ -640,6 +855,21 @@ send_keys_to_term(term_T *term, int c, int typed) return OK; } +/* + * 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 @@ -653,10 +883,6 @@ terminal_loop(void) 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); @@ -665,6 +891,7 @@ terminal_loop(void) /* 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,8 +914,15 @@ terminal_loop(void) 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,6 +938,8 @@ terminal_loop(void) /* * 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,120 +1127,12 @@ handle_pushline(int cols, const VTermScreenCell *cells, void *user) line->sb_cells = p; ++term->tl_scrollback.ga_len; ++term->tl_scrollback_scrolled; + + add_scrollback_line_to_buffer(term); } 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 = { handle_damage, /* damage */ handle_moverect, /* moverect */ @@ -1029,14 +1157,16 @@ term_channel_closed(channel_T *ch) 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; - /* move the lines into the buffer and free the vterm */ - move_scrollback_to_buffer(term); - term_free_vterm(term); + /* 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,8 +1357,9 @@ term_update_window(win_T *wp) VTermState *state; VTermPos pos; - if (term == NULL || term->tl_vterm == NULL) + 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); @@ -1346,6 +1477,18 @@ term_is_finished(buf_T *buf) return buf->b_term != NULL && buf->b_term->tl_vterm == NULL; } +/* + * 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,7 +1593,14 @@ term_get_status_text(term_T *term) char_u *txt; size_t len; - if (term->tl_title != NULL) + 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"); diff --git a/src/version.c b/src/version.c index 7a3e04a174..9cc8e14486 100644 --- a/src/version.c +++ b/src/version.c @@ -769,6 +769,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 813, /**/ 812, /**/ From 0e7885e67d7d798812e23dd8d32e75106f06cb76 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 30 Jul 2017 16:58:56 +0200 Subject: [PATCH 15/24] patch 8.0.0814: file in Filelist does not exist Problem: File in Filelist does not exist. Solution: Remove the line. --- Filelist | 1 - src/version.c | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Filelist b/Filelist index 353cb24d38..5038e67687 100644 --- a/Filelist +++ b/Filelist @@ -200,7 +200,6 @@ SRC_ALL = \ src/libvterm/.gitignore \ src/libvterm/LICENSE \ src/libvterm/Makefile \ - src/libvterm/Makefile.msc \ src/libvterm/README \ src/libvterm/tbl2inc_c.pl \ src/libvterm/vterm.pc.in \ diff --git a/src/version.c b/src/version.c index 9cc8e14486..54ee61fe9c 100644 --- a/src/version.c +++ b/src/version.c @@ -769,6 +769,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 814, /**/ 813, /**/ From 43c007ff225bf5522e07f23f41bfaced573b0e98 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 30 Jul 2017 17:45:37 +0200 Subject: [PATCH 16/24] patch 8.0.0815: terminal window not correctly updated Problem: Terminal window not correctly updated when 'statusline' invokes ":sleep". (NIkolay Pavlov) Solution: Clear got_int. Repeat redrawing when needed. --- src/terminal.c | 6 +++++- src/version.c | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/terminal.c b/src/terminal.c index b0ba8c1902..303566576e 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -54,6 +54,7 @@ * - 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 + * - add 't' to mode() * - when closing window and job has not ended, make terminal hidden? * - when closing window and job has ended, make buffer hidden? * - don't allow exiting Vim when a terminal is still running a job @@ -780,6 +781,7 @@ term_vgetc() ++allow_keys; got_int = FALSE; c = vgetc(); + got_int = FALSE; --no_mapping; --allow_keys; return c; @@ -889,7 +891,9 @@ terminal_loop(void) for (;;) { /* TODO: skip screen update when handling a sequence of keys. */ - update_screen(0); + /* Repeat redrawing in case a message is received while redrawing. */ + while (curwin->w_redr_type != 0) + update_screen(0); update_cursor(curbuf->b_term, FALSE); c = term_vgetc(); diff --git a/src/version.c b/src/version.c index 54ee61fe9c..33e7e3d77b 100644 --- a/src/version.c +++ b/src/version.c @@ -769,6 +769,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 815, /**/ 814, /**/ From f144a3fb73340fe22e08bd1fa77e27c9f55bb0af Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 30 Jul 2017 18:02:12 +0200 Subject: [PATCH 17/24] patch 8.0.0816: crash when using invalid buffer number Problem: Crash when using invalid buffer number. Solution: Check for NULL buffer. (Yasuhiro Matsumoto, closes #1899) --- src/terminal.c | 2 +- src/testdir/test_terminal.vim | 4 ++++ src/version.c | 2 ++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/terminal.c b/src/terminal.c index 303566576e..f526108c59 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -1686,7 +1686,7 @@ term_get_buf(typval_T *argvars) ++emsg_off; buf = get_buf_tv(&argvars[0], FALSE); --emsg_off; - if (buf->b_term == NULL) + if (buf == NULL || buf->b_term == NULL) return NULL; return buf; } diff --git a/src/testdir/test_terminal.vim b/src/testdir/test_terminal.vim index 10fe4af612..acf69be337 100644 --- a/src/testdir/test_terminal.vim +++ b/src/testdir/test_terminal.vim @@ -54,6 +54,9 @@ func Test_terminal_scrape() call assert_equal(1, len(termlist)) call assert_equal(buf, termlist[0]) + " Nothing happens with invalid buffer number + call term_wait(1234) + call term_wait(buf) call Check_123(buf) @@ -64,4 +67,5 @@ func Test_terminal_scrape() call Check_123(buf) exe buf . 'bwipe' + call delete('Xtext') endfunc diff --git a/src/version.c b/src/version.c index 33e7e3d77b..f601bfd71b 100644 --- a/src/version.c +++ b/src/version.c @@ -769,6 +769,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 816, /**/ 815, /**/ From 22aad2f8806acf390568b8e524e53260a322aaa5 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 30 Jul 2017 18:19:46 +0200 Subject: [PATCH 18/24] patch 8.0.0817: cannot get the terminal line at the cursor Problem: Cannot get the line of a terminal window at the cursor. Solution: Make the row argunt optionsl. (Yasuhiro Matsumoto, closes #1898) --- runtime/doc/eval.txt | 19 ++++++++++--------- src/evalfunc.c | 4 ++-- src/terminal.c | 25 ++++++++++++++----------- src/version.c | 2 ++ 4 files changed, 28 insertions(+), 22 deletions(-) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 9d26f361fa..dee7b64fa1 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1,4 +1,4 @@ -*eval.txt* For Vim version 8.0. Last change: 2017 Jul 29 +*eval.txt* For Vim version 8.0. Last change: 2017 Jul 30 VIM REFERENCE MANUAL by Bram Moolenaar @@ -2371,10 +2371,10 @@ tanh({expr}) Float hyperbolic tangent of {expr} tempname() String name for a temporary file term_getattr({attr}, {what} Number get the value of attribute {what} term_getjob({buf}) Job get the job associated with a terminal -term_getline({buf}, {row}) String get a line of text from a terminal +term_getline({buf}[, {row}]) String get a line of text from a terminal term_getsize({buf}) List get the size of a terminal term_list() List get the list of terminal buffers -term_scrape({buf}, {row}) List get row of a terminal screen +term_scrape({buf}[, {row}]) List get row of a terminal screen term_sendkeys({buf}, {keys}) none send keystrokes to a terminal term_start({cmd}, {options}) Job open a terminal window and run a job term_wait({buf}) Number wait for screen to be updated @@ -7914,12 +7914,13 @@ term_getjob({buf}) *term_getjob()* Get the Job associated with terminal window {buf}. {buf} is used as with |term_getsize()|. -term_getline({buf}, {row}) *term_getline()* +term_getline({buf} [, {row}]) *term_getline()* Get a line of text from the terminal window of {buf}. {buf} is used as with |term_getsize()|. The first line has {row} zero. When {row} is invalid an empty - string is returned. + string is returned. When {row} is omitted, the cursor line is + used. term_getsize({buf}) *term_getsize()* Get the size of terminal {buf}. Returns a list with two @@ -7930,17 +7931,17 @@ term_getsize({buf}) *term_getsize()* buffer does not exist or is not a terminal window, an empty list is returned. -term_list(}) *term_list()* +term_list() *term_list()* Return a list with the buffer numbers of all buffers for terminal windows. -term_scrape({buf}, {row}) *term_scrape()* +term_scrape({buf} [, {row}]) *term_scrape()* Get the contents of {row} of terminal screen of {buf}. For {buf} see |term_getsize()|. The first {row} is zero. When {row} is invalid an empty list - is returned. - + is returned. When {row} is omitted the cursor line is used. + Return a List containing a Dict for each screen cell: "chars" character(s) at the cell "fg" foreground color as #rrggbb diff --git a/src/evalfunc.c b/src/evalfunc.c index 7a2e4aad59..f1de7e5c35 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -833,10 +833,10 @@ static struct fst #ifdef FEAT_TERMINAL {"term_getattr", 2, 2, f_term_getattr}, {"term_getjob", 1, 1, f_term_getjob}, - {"term_getline", 2, 2, f_term_getline}, + {"term_getline", 1, 2, f_term_getline}, {"term_getsize", 1, 1, f_term_getsize}, {"term_list", 0, 0, f_term_list}, - {"term_scrape", 2, 2, f_term_scrape}, + {"term_scrape", 1, 2, f_term_scrape}, {"term_sendkeys", 2, 2, f_term_sendkeys}, {"term_start", 1, 2, f_term_start}, {"term_wait", 1, 1, f_term_wait}, diff --git a/src/terminal.c b/src/terminal.c index f526108c59..8febb8d277 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -53,6 +53,7 @@ * :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 term_getcursor() - return cursor position: [row, col, visible] * - add a character in :ls output * - add 't' to mode() * - when closing window and job has not ended, make terminal hidden? @@ -120,7 +121,7 @@ struct terminal_S { garray_T tl_scrollback; int tl_scrollback_scrolled; - pos_T tl_cursor; + VTermPos tl_cursor_pos; int tl_cursor_visible; }; @@ -1020,20 +1021,16 @@ handle_movecursor( { term_T *term = (term_T *)user; win_T *wp; - int is_current = FALSE; + + term->tl_cursor_pos = pos; + term->tl_cursor_visible = visible; FOR_ALL_WINDOWS(wp) { if (wp->w_buffer == term->tl_buffer) - { position_cursor(wp, &pos); - if (wp == curwin) - is_current = TRUE; - } } - - term->tl_cursor_visible = visible; - if (is_current) + if (term->tl_buffer == curbuf) { may_toggle_cursor(term); update_cursor(term, TRUE); @@ -1723,7 +1720,10 @@ f_term_getline(typval_T *argvars, typval_T *rettv) if (buf == NULL) return; term = buf->b_term; - row = (int)get_tv_number(&argvars[1]); + if (argvars[1].v_type == VAR_UNKNOWN) + row = term->tl_cursor_pos.row; + else + row = (int)get_tv_number(&argvars[1]); if (term->tl_vterm == NULL) { @@ -1814,7 +1814,10 @@ f_term_scrape(typval_T *argvars, typval_T *rettv) screen = vterm_obtain_screen(term->tl_vterm); l = rettv->vval.v_list; - pos.row = (int)get_tv_number(&argvars[1]); + if (argvars[1].v_type == VAR_UNKNOWN) + pos.row = term->tl_cursor_pos.row; + else + pos.row = (int)get_tv_number(&argvars[1]); for (pos.col = 0; pos.col < term->tl_cols; ) { dict_T *dcell; diff --git a/src/version.c b/src/version.c index f601bfd71b..21cd3e29aa 100644 --- a/src/version.c +++ b/src/version.c @@ -769,6 +769,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 817, /**/ 816, /**/ From 97870002d30a9846374d1ff7d73fbef351046f20 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 30 Jul 2017 18:28:38 +0200 Subject: [PATCH 19/24] patch 8.0.0818: cannot get the cursor position of a terminal Problem: Cannot get the cursor position of a terminal. Solution: Add term_getcursor(). --- runtime/doc/eval.txt | 14 +++++++++++++ src/evalfunc.c | 1 + src/proto/terminal.pro | 1 + src/terminal.c | 45 ++++++++++++++++++++++++++++++------------ src/version.c | 2 ++ 5 files changed, 50 insertions(+), 13 deletions(-) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index dee7b64fa1..adaa8176e7 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2370,6 +2370,7 @@ tan({expr}) Float tangent of {expr} tanh({expr}) Float hyperbolic tangent of {expr} tempname() String name for a temporary file term_getattr({attr}, {what} Number get the value of attribute {what} +term_getcursor({buf}) List get the cursor position of a terminal term_getjob({buf}) Job get the job associated with a terminal term_getline({buf}[, {row}]) String get a line of text from a terminal term_getsize({buf}) List get the size of a terminal @@ -7910,6 +7911,19 @@ term_getattr({attr}, {what}) *term_getattr()* strike reverse +term_getcursor({buf}) *term_getcursor()* + Get the cusor position of terminal {buf}. Returns a list with + three numbers: [rows, cols, visible]. "rows" and "cols" are + zero based. "visible" is one when the cursor is visible, zero + when it is hidden. + + This is the cursor position of the terminal itself, not of the + Vim window. + + {buf} must be the buffer number of a terminal window. If the + buffer does not exist or is not a terminal window, an empty + list is returned. + term_getjob({buf}) *term_getjob()* Get the Job associated with terminal window {buf}. {buf} is used as with |term_getsize()|. diff --git a/src/evalfunc.c b/src/evalfunc.c index f1de7e5c35..2c11de1916 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -832,6 +832,7 @@ static struct fst {"tempname", 0, 0, f_tempname}, #ifdef FEAT_TERMINAL {"term_getattr", 2, 2, f_term_getattr}, + {"term_getcursor", 1, 1, f_term_getcursor}, {"term_getjob", 1, 1, f_term_getjob}, {"term_getline", 1, 2, f_term_getline}, {"term_getsize", 1, 1, f_term_getsize}, diff --git a/src/proto/terminal.pro b/src/proto/terminal.pro index 6f5b3ab707..63cda59caf 100644 --- a/src/proto/terminal.pro +++ b/src/proto/terminal.pro @@ -16,6 +16,7 @@ int term_get_attr(buf_T *buf, linenr_T lnum, int col); char_u *term_get_status_text(term_T *term); int set_ref_in_term(int copyID); void f_term_getattr(typval_T *argvars, typval_T *rettv); +void f_term_getcursor(typval_T *argvars, typval_T *rettv); void f_term_getjob(typval_T *argvars, typval_T *rettv); void f_term_getline(typval_T *argvars, typval_T *rettv); void f_term_getsize(typval_T *argvars, typval_T *rettv); diff --git a/src/terminal.c b/src/terminal.c index 8febb8d277..45c6175708 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -53,7 +53,6 @@ * :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 term_getcursor() - return cursor position: [row, col, visible] * - add a character in :ls output * - add 't' to mode() * - when closing window and job has not ended, make terminal hidden? @@ -1636,6 +1635,24 @@ set_ref_in_term(int copyID) return abort; } +/* + * Get the buffer from the first argument in "argvars". + * Returns NULL when the buffer is not for a terminal window. + */ + static buf_T * +term_get_buf(typval_T *argvars) +{ + buf_T *buf; + + (void)get_tv_number(&argvars[0]); /* issue errmsg if type error */ + ++emsg_off; + buf = get_buf_tv(&argvars[0], FALSE); + --emsg_off; + if (buf == NULL || buf->b_term == NULL) + return NULL; + return buf; +} + /* * "term_getattr(attr, name)" function */ @@ -1671,21 +1688,23 @@ f_term_getattr(typval_T *argvars, typval_T *rettv) } /* - * Get the buffer from the first argument in "argvars". - * Returns NULL when the buffer is not for a terminal window. + * "term_getcursor(buf)" function */ - static buf_T * -term_get_buf(typval_T *argvars) + void +f_term_getcursor(typval_T *argvars, typval_T *rettv) { - buf_T *buf; + buf_T *buf = term_get_buf(argvars); + list_T *l; - (void)get_tv_number(&argvars[0]); /* issue errmsg if type error */ - ++emsg_off; - buf = get_buf_tv(&argvars[0], FALSE); - --emsg_off; - if (buf == NULL || buf->b_term == NULL) - return NULL; - return buf; + if (rettv_list_alloc(rettv) == FAIL) + return; + if (buf == NULL) + return; + + l = rettv->vval.v_list; + list_append_number(l, buf->b_term->tl_cursor_pos.row); + list_append_number(l, buf->b_term->tl_cursor_pos.col); + list_append_number(l, buf->b_term->tl_cursor_visible); } /* diff --git a/src/version.c b/src/version.c index 21cd3e29aa..5b831d4187 100644 --- a/src/version.c +++ b/src/version.c @@ -769,6 +769,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 818, /**/ 817, /**/ From 0e23e9c5e74dbf0e5eec710c41c9eaed35794682 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 30 Jul 2017 18:47:19 +0200 Subject: [PATCH 20/24] patch 8.0.0819: cursor not positioned in terminal window Problem: After changing current window the cursor position in the terminal window is not updated. Solution: Set w_wrow, w_wcol and w_valid. --- src/terminal.c | 16 +++++++++------- src/version.c | 2 ++ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/terminal.c b/src/terminal.c index 45c6175708..be76161e51 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -857,6 +857,14 @@ send_keys_to_term(term_T *term, int c, int typed) return OK; } + static void +position_cursor(win_T *wp, VTermPos *pos) +{ + wp->w_wrow = MIN(pos->row, MAX(0, wp->w_height - 1)); + wp->w_wcol = MIN(pos->col, MAX(0, wp->w_width - 1)); + wp->w_valid |= (VALID_WCOL|VALID_WROW); +} + /* * Returns TRUE if the current window contains a terminal and we are sending * keys to the job. @@ -887,6 +895,7 @@ terminal_loop(void) if (*curwin->w_p_tk != NUL) termkey = string_to_key(curwin->w_p_tk, TRUE); + position_cursor(curwin, &curbuf->b_term->tl_cursor_pos); for (;;) { @@ -971,13 +980,6 @@ term_job_ended(job_T *job) } } - static void -position_cursor(win_T *wp, VTermPos *pos) -{ - wp->w_wrow = MIN(pos->row, MAX(0, wp->w_height - 1)); - wp->w_wcol = MIN(pos->col, MAX(0, wp->w_width - 1)); -} - static void may_toggle_cursor(term_T *term) { diff --git a/src/version.c b/src/version.c index 5b831d4187..2d4c50e0ef 100644 --- a/src/version.c +++ b/src/version.c @@ -769,6 +769,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 819, /**/ 818, /**/ From 12d93ee26d4290321ed0cc3d0493b966d1475a66 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 30 Jul 2017 19:02:02 +0200 Subject: [PATCH 21/24] patch 8.0.0820: GUI: cursor in terminal window lags behind Problem: GUI: cursor in terminal window lags behind. Solution: call gui_update_cursor() under different conditions. (Ozaki Kiichi, closes #1893) --- src/terminal.c | 14 +++++++++----- src/version.c | 2 ++ 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/terminal.c b/src/terminal.c index be76161e51..b289f6c994 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -51,6 +51,9 @@ * job finishes). * - add option values to the command: * :term <24x80> vim notes.txt + * - support different cursor shapes, colors and attributes + * - make term_getcursor() return type (none/block/bar/underline) and + * attributes (color, blink, etc.) * - 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 @@ -359,7 +362,7 @@ update_cursor(term_T *term, int redraw) cursor_on(); out_flush(); #ifdef FEAT_GUI - if (gui.in_use && term->tl_cursor_visible) + if (gui.in_use) gui_update_cursor(FALSE, FALSE); #endif } @@ -1034,7 +1037,7 @@ handle_movecursor( if (term->tl_buffer == curbuf) { may_toggle_cursor(term); - update_cursor(term, TRUE); + update_cursor(term, term->tl_cursor_visible); } return 1; @@ -1180,11 +1183,12 @@ term_channel_closed(channel_T *ch) /* Need to break out of vgetc(). */ ins_char_typebuf(K_IGNORE); - if (curbuf->b_term != NULL) + term = curbuf->b_term; + if (term != NULL) { - if (curbuf->b_term->tl_job == ch->ch_job) + if (term->tl_job == ch->ch_job) maketitle(); - update_cursor(curbuf->b_term, TRUE); + update_cursor(term, term->tl_cursor_visible); } } } diff --git a/src/version.c b/src/version.c index 2d4c50e0ef..8733db28ed 100644 --- a/src/version.c +++ b/src/version.c @@ -769,6 +769,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 820, /**/ 819, /**/ From b000e328efcf859d14454ffd241d44f6d14f300b Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 30 Jul 2017 19:38:21 +0200 Subject: [PATCH 22/24] patch 8.0.0821: cannot get the title and status of a terminal window Problem: Cannot get the title and status of a terminal window. Solution: Implement term_gettitle() and term_getstatus(). --- runtime/doc/eval.txt | 22 ++++++++++++++++++++ src/evalfunc.c | 2 ++ src/proto/terminal.pro | 2 ++ src/terminal.c | 46 +++++++++++++++++++++++++++++++++++++----- src/version.c | 2 ++ 5 files changed, 69 insertions(+), 5 deletions(-) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index adaa8176e7..f43e7164a4 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2374,6 +2374,8 @@ term_getcursor({buf}) List get the cursor position of a terminal term_getjob({buf}) Job get the job associated with a terminal term_getline({buf}[, {row}]) String get a line of text from a terminal term_getsize({buf}) List get the size of a terminal +term_getstatus({buf}) String get the status of a terminal +term_gettitle({buf}) String get the title of a terminal term_list() List get the list of terminal buffers term_scrape({buf}[, {row}]) List get row of a terminal screen term_sendkeys({buf}, {keys}) none send keystrokes to a terminal @@ -7945,6 +7947,26 @@ term_getsize({buf}) *term_getsize()* buffer does not exist or is not a terminal window, an empty list is returned. +term_getstatus({buf}) *term_getstatus()* + Get the status of terminal {buf}. This returns a comma + separated list of these items: + running job is running + finished job has finished + terminal in Terminal-Normal mode + One of "running" or "finished" is always present. + + {buf} must be the buffer number of a terminal window. If the + buffer does not exist or is not a terminal window, an empty + string is returned. + +term_gettitle({buf}) *term_gettitle()* + Get the title of terminal {buf}. This is the title that the + job in the terminal has set. + + {buf} must be the buffer number of a terminal window. If the + buffer does not exist or is not a terminal window, an empty + string is returned. + term_list() *term_list()* Return a list with the buffer numbers of all buffers for terminal windows. diff --git a/src/evalfunc.c b/src/evalfunc.c index 2c11de1916..fc0e05dad6 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -836,6 +836,8 @@ static struct fst {"term_getjob", 1, 1, f_term_getjob}, {"term_getline", 1, 2, f_term_getline}, {"term_getsize", 1, 1, f_term_getsize}, + {"term_getstatus", 1, 1, f_term_getstatus}, + {"term_gettitle", 1, 1, f_term_gettitle}, {"term_list", 0, 0, f_term_list}, {"term_scrape", 1, 2, f_term_scrape}, {"term_sendkeys", 2, 2, f_term_sendkeys}, diff --git a/src/proto/terminal.pro b/src/proto/terminal.pro index 63cda59caf..24789293df 100644 --- a/src/proto/terminal.pro +++ b/src/proto/terminal.pro @@ -20,6 +20,8 @@ void f_term_getcursor(typval_T *argvars, typval_T *rettv); void f_term_getjob(typval_T *argvars, typval_T *rettv); void f_term_getline(typval_T *argvars, typval_T *rettv); void f_term_getsize(typval_T *argvars, typval_T *rettv); +void f_term_getstatus(typval_T *argvars, typval_T *rettv); +void f_term_gettitle(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); diff --git a/src/terminal.c b/src/terminal.c index b289f6c994..d04b99ec69 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -36,15 +36,11 @@ * 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 @@ -560,7 +556,7 @@ term_convert_key(term_T *term, int c, char *buf) } /* - * Return TRUE if the job for "buf" is still running. + * Return TRUE if the job for "term" is still running. */ static int term_job_running(term_T *term) @@ -1798,6 +1794,46 @@ f_term_getsize(typval_T *argvars, typval_T *rettv) list_append_number(l, buf->b_term->tl_cols); } +/* + * "term_getstatus(buf)" function + */ + void +f_term_getstatus(typval_T *argvars, typval_T *rettv) +{ + buf_T *buf = term_get_buf(argvars); + term_T *term; + char_u val[100]; + + rettv->v_type = VAR_STRING; + if (buf == NULL) + return; + term = buf->b_term; + + if (term_job_running(term)) + STRCPY(val, "running"); + else + STRCPY(val, "finished"); + if (term->tl_terminal_mode) + STRCAT(val, ",terminal"); + rettv->vval.v_string = vim_strsave(val); +} + +/* + * "term_gettitle(buf)" function + */ + void +f_term_gettitle(typval_T *argvars, typval_T *rettv) +{ + buf_T *buf = term_get_buf(argvars); + + rettv->v_type = VAR_STRING; + if (buf == NULL) + return; + + if (buf->b_term->tl_title != NULL) + rettv->vval.v_string = vim_strsave(buf->b_term->tl_title); +} + /* * "term_list()" function */ diff --git a/src/version.c b/src/version.c index 8733db28ed..875119f416 100644 --- a/src/version.c +++ b/src/version.c @@ -769,6 +769,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 821, /**/ 820, /**/ From d09be321422669fc32d4d31a4ac7621c89136f7b Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 30 Jul 2017 21:37:58 +0200 Subject: [PATCH 23/24] patch 8.0.0822: Test_with_partial_callback is a tiny bit flaky Problem: Test_with_partial_callback is a tiny bit flaky. Solution: Add it to the list of flaky tests. --- src/testdir/runtest.vim | 1 + src/version.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/testdir/runtest.vim b/src/testdir/runtest.vim index c57ec5f642..b1d9d947f7 100644 --- a/src/testdir/runtest.vim +++ b/src/testdir/runtest.vim @@ -233,6 +233,7 @@ let s:flaky = [ \ 'Test_quoteplus()', \ 'Test_quotestar()', \ 'Test_reltime()', + \ 'Test_with_partial_callback()', \ ] " Locate Test_ functions and execute them. diff --git a/src/version.c b/src/version.c index 875119f416..145fcdc7c8 100644 --- a/src/version.c +++ b/src/version.c @@ -769,6 +769,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 822, /**/ 821, /**/ From c9456cec0cfe956e083891187b791343e732207b Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 30 Jul 2017 21:46:04 +0200 Subject: [PATCH 24/24] patch 8.0.0823: cannot paste text into a terminal window Problem: Cannot paste text into a terminal window. Solution: Make CTRL-W " work. --- src/terminal.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++-- src/version.c | 2 ++ 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/src/terminal.c b/src/terminal.c index d04b99ec69..1f155dbab6 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -36,7 +36,6 @@ * that buffer, attributes come from the scrollback buffer tl_scrollback. * * TODO: - * - 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 @@ -67,6 +66,7 @@ * mouse in the Terminal window for copy/paste. * - when 'encoding' is not utf-8, or the job is using another encoding, setup * conversions. + * - update ":help function-list" for terminal functions. * - In the GUI use a terminal emulator for :!cmd. */ @@ -864,6 +864,50 @@ position_cursor(win_T *wp, VTermPos *pos) wp->w_valid |= (VALID_WCOL|VALID_WROW); } +/* + * Handle CTRL-W "": send register contents to the job. + */ + static void +term_paste_register(int prev_c UNUSED) +{ + int c; + list_T *l; + listitem_T *item; + long reglen = 0; + int type; + +#ifdef FEAT_CMDL_INFO + if (add_to_showcmd(prev_c)) + if (add_to_showcmd('"')) + out_flush(); +#endif + c = term_vgetc(); +#ifdef FEAT_CMDL_INFO + clear_showcmd(); +#endif + + /* CTRL-W "= prompt for expression to evaluate. */ + if (c == '=' && get_expr_register() != '=') + return; + + l = (list_T *)get_reg_contents(c, GREG_LIST); + if (l != NULL) + { + type = get_reg_type(c, ®len); + for (item = l->lv_first; item != NULL; item = item->li_next) + { + char_u *s = get_tv_string(&item->li_tv); + + channel_send(curbuf->b_term->tl_job->jv_channel, PART_IN, + s, STRLEN(s), NULL); + if (item->li_next != NULL || type == MLINE) + channel_send(curbuf->b_term->tl_job->jv_channel, PART_IN, + (char_u *)"\r", 1, NULL); + } + list_free(l); + } +} + /* * Returns TRUE if the current window contains a terminal and we are sending * keys to the job. @@ -912,6 +956,8 @@ terminal_loop(void) if (c == (termkey == 0 ? Ctrl_W : termkey)) { + int prev_c = c; + #ifdef FEAT_CMDL_INFO if (add_to_showcmd(c)) out_flush(); @@ -930,11 +976,16 @@ terminal_loop(void) /* "CTRL-W .": send CTRL-W to the job */ c = Ctrl_W; } - else if (termkey == 0 && c == 'N') + else if (c == 'N') { term_enter_terminal_mode(); return FAIL; } + else if (c == '"') + { + term_paste_register(prev_c); + continue; + } else if (termkey == 0 || c != termkey) { stuffcharReadbuff(Ctrl_W); diff --git a/src/version.c b/src/version.c index 145fcdc7c8..9c94c988f6 100644 --- a/src/version.c +++ b/src/version.c @@ -769,6 +769,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 823, /**/ 822, /**/