mirror of
https://github.com/vim/vim.git
synced 2025-07-26 11:04:33 -04:00
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)
This commit is contained in:
parent
70229f951f
commit
c6df10e5d3
@ -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
|
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||||
@ -2369,12 +2369,15 @@ tagfiles() List tags files used
|
|||||||
tan({expr}) Float tangent of {expr}
|
tan({expr}) Float tangent of {expr}
|
||||||
tanh({expr}) Float hyperbolic tangent of {expr}
|
tanh({expr}) Float hyperbolic tangent of {expr}
|
||||||
tempname() String name for a temporary file
|
tempname() String name for a temporary file
|
||||||
term_getsize() Dict get the size of a terminal
|
term_getattr({attr}, {what} Number get the value of attribute {what}
|
||||||
term_open() Job open a terminal window and run a job
|
term_getjob({buf}) Job get the job associated with a terminal
|
||||||
term_scrape() List inspect terminal screen
|
term_getline({buf}, {row}) String get a line of text from a terminal
|
||||||
term_sendkeys() Number send keystrokes to a terminal
|
term_getsize({buf}) List get the size of a terminal
|
||||||
term_setsize() Number set the size of a terminal
|
term_list() List get the list of terminal buffers
|
||||||
term_wait() Number wait for screen to be updated
|
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})
|
test_alloc_fail({id}, {countdown}, {repeat})
|
||||||
none make memory allocation fail
|
none make memory allocation fail
|
||||||
test_autochdir() none enable 'autochdir' during startup
|
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'
|
For MS-Windows forward slashes are used when the 'shellslash'
|
||||||
option is set or when 'shellcmdflag' starts with '-'.
|
option is set or when 'shellcmdflag' starts with '-'.
|
||||||
|
|
||||||
term_getsize() *term_getsize()*
|
term_getattr({attr}, {what}) *term_getattr()*
|
||||||
Get the size of a terminal. NOT IMPLEMENTED YET
|
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()*
|
term_getjob({buf}) *term_getjob()*
|
||||||
Open a terminal window and run a job. NOT IMPLEMENTED YET
|
Get the Job associated with terminal window {buf}.
|
||||||
|
{buf} is used as with |term_getsize()|.
|
||||||
|
|
||||||
term_scrape() *term_scrape()*
|
term_getline({buf}, {row}) *term_getline()*
|
||||||
Inspect terminal screen. NOT IMPLEMENTED YET
|
Get a line of text from the terminal window of {buf}.
|
||||||
|
{buf} is used as with |term_getsize()|.
|
||||||
|
|
||||||
term_sendkeys() *term_sendkeys()*
|
The first line has {row} zero. When {row} is invalid an empty
|
||||||
Send keystrokes to a terminal. NOT IMPLEMENTED YET
|
string is returned.
|
||||||
|
|
||||||
term_setsize() *term_setsize()*
|
term_getsize({buf}) *term_getsize()*
|
||||||
Set the size of a terminal. NOT IMPLEMENTED YET
|
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()*
|
{buf} must be the buffer number of a terminal window. If the
|
||||||
Wait for screen to be updated. NOT IMPLEMENTED YET
|
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, "\<c-x>"
|
||||||
|
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()*
|
test_alloc_fail({id}, {countdown}, {repeat}) *test_alloc_fail()*
|
||||||
This is for testing: If the memory allocation with {id} is
|
This is for testing: If the memory allocation with {id} is
|
||||||
|
@ -2256,6 +2256,7 @@ test_arglist \
|
|||||||
test_tagjump \
|
test_tagjump \
|
||||||
test_taglist \
|
test_taglist \
|
||||||
test_tcl \
|
test_tcl \
|
||||||
|
test_terminal \
|
||||||
test_textobjects \
|
test_textobjects \
|
||||||
test_timers \
|
test_timers \
|
||||||
test_true_false \
|
test_true_false \
|
||||||
|
@ -830,6 +830,17 @@ static struct fst
|
|||||||
{"tanh", 1, 1, f_tanh},
|
{"tanh", 1, 1, f_tanh},
|
||||||
#endif
|
#endif
|
||||||
{"tempname", 0, 0, f_tempname},
|
{"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_alloc_fail", 3, 3, f_test_alloc_fail},
|
||||||
{"test_autochdir", 0, 0, f_test_autochdir},
|
{"test_autochdir", 0, 0, f_test_autochdir},
|
||||||
{"test_garbagecollect_now", 0, 0, f_test_garbagecollect_now},
|
{"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.
|
* Get buffer by number or pattern.
|
||||||
*/
|
*/
|
||||||
static buf_T *
|
buf_T *
|
||||||
get_buf_tv(typval_T *tv, int curtab_only)
|
get_buf_tv(typval_T *tv, int curtab_only)
|
||||||
{
|
{
|
||||||
char_u *name = tv->vval.v_string;
|
char_u *name = tv->vval.v_string;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
/* evalfunc.c */
|
/* 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_function_name(expand_T *xp, int idx);
|
||||||
char_u *get_expr_name(expand_T *xp, int idx);
|
char_u *get_expr_name(expand_T *xp, int idx);
|
||||||
int find_internal_func(char_u *name);
|
int find_internal_func(char_u *name);
|
||||||
|
@ -3,6 +3,7 @@ void ex_terminal(exarg_T *eap);
|
|||||||
void free_terminal(buf_T *buf);
|
void free_terminal(buf_T *buf);
|
||||||
void write_to_term(buf_T *buffer, char_u *msg, channel_T *channel);
|
void write_to_term(buf_T *buffer, char_u *msg, channel_T *channel);
|
||||||
int terminal_loop(void);
|
int terminal_loop(void);
|
||||||
|
void term_job_ended(job_T *job);
|
||||||
void term_channel_closed(channel_T *ch);
|
void term_channel_closed(channel_T *ch);
|
||||||
int term_update_window(win_T *wp);
|
int term_update_window(win_T *wp);
|
||||||
int term_is_finished(buf_T *buf);
|
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);
|
int term_get_attr(buf_T *buf, linenr_T lnum, int col);
|
||||||
char_u *term_get_status_text(term_T *term);
|
char_u *term_get_status_text(term_T *term);
|
||||||
int set_ref_in_term(int copyID);
|
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 : */
|
/* vim: set ft=c : */
|
||||||
|
483
src/terminal.c
483
src/terminal.c
@ -54,14 +54,6 @@
|
|||||||
* - support minimal size when 'termsize' is empty?
|
* - support minimal size when 'termsize' is empty?
|
||||||
* - implement "term" for job_start(): more job options when starting a
|
* - implement "term" for job_start(): more job options when starting a
|
||||||
* terminal.
|
* 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
|
* - when 'encoding' is not utf-8, or the job is using another encoding, setup
|
||||||
* conversions.
|
* conversions.
|
||||||
* - In the GUI use a terminal emulator for :!cmd.
|
* - In the GUI use a terminal emulator for :!cmd.
|
||||||
@ -69,7 +61,7 @@
|
|||||||
|
|
||||||
#include "vim.h"
|
#include "vim.h"
|
||||||
|
|
||||||
#ifdef FEAT_TERMINAL
|
#if defined(FEAT_TERMINAL) || defined(PROTO)
|
||||||
|
|
||||||
#ifdef WIN3264
|
#ifdef WIN3264
|
||||||
# define MIN(x,y) (x < y ? x : y)
|
# 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 */
|
int tl_dirty_row_end; /* row below last one to update */
|
||||||
|
|
||||||
garray_T tl_scrollback;
|
garray_T tl_scrollback;
|
||||||
|
int tl_scrollback_scrolled;
|
||||||
|
|
||||||
pos_T tl_cursor;
|
pos_T tl_cursor;
|
||||||
int tl_cursor_visible;
|
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".
|
* Return the number of bytes in "buf".
|
||||||
*/
|
*/
|
||||||
static int
|
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;
|
VTermKey key = VTERM_KEY_NONE;
|
||||||
VTermModifier mod = VTERM_MOD_NONE;
|
VTermModifier mod = VTERM_MOD_NONE;
|
||||||
|
|
||||||
@ -516,6 +509,76 @@ term_vgetc()
|
|||||||
return c;
|
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.
|
* Wait for input and send it to the job.
|
||||||
* Return when the start of a CTRL-W command is typed or anything else that
|
* Return when the start of a CTRL-W command is typed or anything else that
|
||||||
@ -526,11 +589,7 @@ term_vgetc()
|
|||||||
int
|
int
|
||||||
terminal_loop(void)
|
terminal_loop(void)
|
||||||
{
|
{
|
||||||
char buf[KEY_BUF_LEN];
|
|
||||||
int c;
|
int c;
|
||||||
size_t len;
|
|
||||||
static int mouse_was_outside = FALSE;
|
|
||||||
int dragging_outside = FALSE;
|
|
||||||
int termkey = 0;
|
int termkey = 0;
|
||||||
|
|
||||||
if (curbuf->b_term->tl_vterm == NULL || !term_job_running(curbuf->b_term))
|
if (curbuf->b_term->tl_vterm == NULL || !term_job_running(curbuf->b_term))
|
||||||
@ -576,60 +635,41 @@ terminal_loop(void)
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (send_keys_to_term(curbuf->b_term, c, TRUE) != OK)
|
||||||
/* Catch keys that need to be handled as in Normal mode. */
|
|
||||||
switch (c)
|
|
||||||
{
|
|
||||||
case NUL:
|
|
||||||
case K_ZERO:
|
|
||||||
stuffcharReadbuff(c);
|
|
||||||
return OK;
|
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);
|
|
||||||
}
|
}
|
||||||
return FAIL;
|
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
|
static void
|
||||||
position_cursor(win_T *wp, VTermPos *pos)
|
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_cols = len;
|
||||||
line->sb_cells = p;
|
line->sb_cells = p;
|
||||||
++term->tl_scrollback.ga_len;
|
++term->tl_scrollback.ga_len;
|
||||||
|
++term->tl_scrollback_scrolled;
|
||||||
}
|
}
|
||||||
return 0; /* ignored */
|
return 0; /* ignored */
|
||||||
}
|
}
|
||||||
@ -916,6 +957,7 @@ static VTermScreenCallbacks screen_callbacks = {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Called when a channel has been closed.
|
* Called when a channel has been closed.
|
||||||
|
* If this was a channel for a terminal window then finish it up.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
term_channel_closed(channel_T *ch)
|
term_channel_closed(channel_T *ch)
|
||||||
@ -1080,8 +1122,6 @@ cell2attr(VTermScreenCell *cell)
|
|||||||
attr |= HL_STANDOUT;
|
attr |= HL_STANDOUT;
|
||||||
if (cell->attrs.reverse)
|
if (cell->attrs.reverse)
|
||||||
attr |= HL_INVERSE;
|
attr |= HL_INVERSE;
|
||||||
if (cell->attrs.strike)
|
|
||||||
attr |= HL_UNDERLINE;
|
|
||||||
|
|
||||||
#ifdef FEAT_GUI
|
#ifdef FEAT_GUI
|
||||||
if (gui.in_use)
|
if (gui.in_use)
|
||||||
@ -1384,8 +1424,315 @@ set_ref_in_term(int copyID)
|
|||||||
return abort;
|
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
|
# ifdef WIN3264
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
* 2. MS-Windows implementation.
|
||||||
|
*/
|
||||||
|
|
||||||
#define WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN 1ul
|
#define WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN 1ul
|
||||||
#define WINPTY_SPAWN_FLAG_EXIT_AFTER_SHUTDOWN 2ull
|
#define WINPTY_SPAWN_FLAG_EXIT_AFTER_SHUTDOWN 2ull
|
||||||
|
|
||||||
@ -1404,10 +1751,6 @@ void (*winpty_error_free)(void*);
|
|||||||
LPCWSTR (*winpty_error_msg)(void*);
|
LPCWSTR (*winpty_error_msg)(void*);
|
||||||
BOOL (*winpty_set_size)(void*, int, int, void*);
|
BOOL (*winpty_set_size)(void*, int, int, void*);
|
||||||
|
|
||||||
/**************************************
|
|
||||||
* 2. MS-Windows implementation.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define WINPTY_DLL "winpty.dll"
|
#define WINPTY_DLL "winpty.dll"
|
||||||
|
|
||||||
static HINSTANCE hWinPtyDLL = NULL;
|
static HINSTANCE hWinPtyDLL = NULL;
|
||||||
|
@ -197,6 +197,7 @@ NEW_TESTS = test_arabic.res \
|
|||||||
test_syntax.res \
|
test_syntax.res \
|
||||||
test_system.res \
|
test_system.res \
|
||||||
test_tcl.res \
|
test_tcl.res \
|
||||||
|
test_terminal.res \
|
||||||
test_textobjects.res \
|
test_textobjects.res \
|
||||||
test_undo.res \
|
test_undo.res \
|
||||||
test_usercommands.res \
|
test_usercommands.res \
|
||||||
|
67
src/testdir/test_terminal.vim
Normal file
67
src/testdir/test_terminal.vim
Normal file
@ -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(["\<Esc>[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
|
@ -769,6 +769,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
803,
|
||||||
/**/
|
/**/
|
||||||
802,
|
802,
|
||||||
/**/
|
/**/
|
||||||
|
Loading…
x
Reference in New Issue
Block a user