Compare commits

...

20 Commits

Author SHA1 Message Date
Bram Moolenaar
0aed9a2e2e patch 8.0.0971: 'winptydll' missing from :options
Problem:    'winptydll' missing from :options.
Solution:   Add the entry.
2017-08-19 23:18:02 +02:00
Bram Moolenaar
d6a7b3e6bb patch 8.0.0970: passing invalid highlight id
Problem:    if there is no StatusLine highlighting and there is StatusLineNC
            or StatusLineTermNC highlighting then an invalid highlight id is
            passed to combine_stl_hlt(). (Coverity)
Solution:   Check id_S to be -1 instead of zero.
2017-08-19 21:35:35 +02:00
Bram Moolenaar
dc926dd0dd patch 8.0.0969: Coverity warning for unused return value
Problem:    Coverity warning for unused return value.
Solution:   Add (void) to avoid the warning.
2017-08-19 21:26:44 +02:00
Bram Moolenaar
77ac9b5c62 patch 8.0.0968: crash when switching terminal modes
Problem:    Crash when switching terminal modes. (Nikolai Pavlov)
Solution:   Check that there are scrollback lines.
2017-08-19 21:23:05 +02:00
Bram Moolenaar
93c92eff26 patch 8.0.0967: using a terminal may cause the cursor to blink
Problem:    Using a terminal may cause the cursor to blink.
Solution:   Do not set t_vs, since we cannot restore the old blink state.
2017-08-19 21:11:57 +02:00
Bram Moolenaar
fc8bec0be4 patch 8.0.0966: build failure without terminal feature
Problem:    Build failure without terminal feature.
Solution:   Move #endif.
2017-08-19 19:57:34 +02:00
Bram Moolenaar
3eee06e7d4 patch 8.0.0965: not restoring cursor shape after it was set in a terminal
Problem:    The cursor shape is not reset after it was changed in a terminal.
Solution:   Request the original cursor shape and restore it.  Add t_RS.
            Do not add t_SH for now, it does not work properly.
2017-08-19 19:40:50 +02:00
Bram Moolenaar
683b796725 patch 8.0.0964: channel write buffer does not work with poll()
Problem:    Channel write buffer does not work with poll().
Solution:   Use the same mechanism as with select().
2017-08-19 15:51:59 +02:00
Bram Moolenaar
d21f8b54b2 patch 8.0.0963: terminal test fails on MacOS
Problem:    Terminal test fails on MacOS. (chdiza)
Solution:   Wait for the shell to echo the characters. (closes #1991)
2017-08-19 15:40:01 +02:00
Bram Moolenaar
9aa1569128 patch 8.0.0962: crash with virtualedit and joining lines
Problem:    Crash with virtualedit and joining lines. (Joshua T Corbin, Neovim
            #6726)
Solution:   When using a mark check that coladd is valid.
2017-08-19 15:05:32 +02:00
Bram Moolenaar
98ebd2bbec patch 8.0.0961: the script to build the installer does not include winpty
Problem:    The script to build the installer does not include winpty.
Solution:   Add winpty32.dll and winpty-agent.exe like diff.exe
2017-08-19 13:29:19 +02:00
Bram Moolenaar
8e539c51c3 patch 8.0.0960: job in terminal does not get CTRL-C
Problem:    Job in terminal does not get CTRL-C, we send a SIGINT instead.
Solution:   Don't call may_send_sigint() on CTRL-C.  Make CTRL-W CTRL-C end
            the job.
2017-08-18 22:57:06 +02:00
Bram Moolenaar
f66a2cda2c patch 8.0.0959: build failure on MS-Windows
Problem:    Build failure on MS-Windows.
Solution:   Use ioctlsocket() instead of fcntl().
2017-08-18 21:53:22 +02:00
Bram Moolenaar
ea5d6fadbb patch 8.0.0958: terminal test fails on Windows when winpty dll is missing
Problem:    The terminal test fails on MS-Windows when compiled with the
            terminal feature but the winpty DLL is missing.
Solution:   Check if the terminal feature works. (Ken Takata)
2017-08-18 21:07:11 +02:00
Bram Moolenaar
97bd5e6527 patch 8.0.0957: a terminal job can deadlock when sending many keys
Problem:    When term_sendkeys() sends many keys it may get stuck in writing
            to the job.
Solution:   Make the write non-blocking, buffer keys to be sent.
2017-08-18 20:50:30 +02:00
Bram Moolenaar
cfce71710b patch 8.0.0956: scrolling in a terminal window has flicker
Problem:    Scrolling in a terminal hwindow as flicker when the Normal
            background differs from the terminal window background.
Solution:   Set the attribute to clear with.
2017-08-17 20:31:48 +02:00
Bram Moolenaar
82de3c2c03 patch 8.0.0955: Test_existent_file() fails on some file systems
Problem:    Test_existent_file() fails on some file systems.
Solution:   Run the test again with a sleep when the test fails without a
            sleep. (James McCoy, closes #1984)
2017-08-17 17:35:36 +02:00
Bram Moolenaar
bc906e445c patch 8.0.0954: /proc/self/exe might be a relative path
Problem:    /proc/self/exe might be a relative path.
Solution:   Make the path a full path. (James McCoy, closes #1983)
2017-08-17 17:21:05 +02:00
Bram Moolenaar
f5be7cd016 patch 8.0.0953: get "no write since last change" error in terminal window
Problem:    Get "no write since last change" error in terminal window.
Solution:   Use another message when closing a terminal window. Make ":quit!"
            also end the job.
2017-08-17 16:55:13 +02:00
Bram Moolenaar
a83e3962ac patch 8.0.0952: has('terminal') does not check existence of dll file
Problem:    MS-Windows: has('terminal') does not check existence of dll file.
Solution:   Check if the winpty dll file can be loaded. (Ken Takata)
2017-08-17 14:39:07 +02:00
33 changed files with 817 additions and 354 deletions

View File

@@ -191,13 +191,16 @@ Section "Vim executables and runtime files"
File ${VIMSRC}\vimrun.exe
File /oname=tee.exe ${VIMSRC}\teew32.exe
File /oname=xxd.exe ${VIMSRC}\xxdw32.exe
File ${VIMTOOLS}\diff.exe
File ${VIMRT}\vimtutor.bat
File ${VIMRT}\README.txt
File ..\uninstal.txt
File ${VIMRT}\*.vim
File ${VIMRT}\rgb.txt
File ${VIMTOOLS}\diff.exe
File ${VIMTOOLS}\winpty32.dll
File ${VIMTOOLS}\winpty-agent.exe
SetOutPath $0\colors
File ${VIMRT}\colors\*.*

View File

@@ -52,6 +52,7 @@ Special in the terminal window: *CTRL-W_.* *CTRL-W_N*
CTRL-W " {reg} paste register {reg} *CTRL-W_quote*
Also works with the = register to insert the result of
evaluating an expression.
CTRL-W CTRL-C ends the job, see below |t_CTRL-W_CTRL-C|
See option 'termkey' for specifying another key instead of CTRL-W that
will work like CTRL-W. However, typing 'termkey' twice sends 'termkey' to
@@ -62,16 +63,29 @@ the job. For example:
'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
'termkey' CTRL-C same as |t_CTRL-W_CTRL-C|
*t_CTRL-\_CTRL-N*
The special key combination CTRL-\ CTRL-N can be used to switch to Normal
mode, just like this works in any other mode.
*t_CTRL-W_CTRL-C*
CTRL-W CTRL-C can be typed to forcefully end the job. On MS-Windows a
CTRL-BREAK will also kill the job.
If you type CTRL-C the effect depends on what the pty has been configured to
do. For simple commands this causes a SIGINT to be sent to the job, which
would end it. Other commands may ignore the SIGINT or handle the CTRL-C
themselves (like Vim does).
Size ~
Size and color ~
See option 'termsize' for controlling the size of the terminal window.
(TODO: scrolling when the terminal is larger than the window)
The terminal uses the 'background' option to decide whether the terminal
window will start with a white or black background. The job running in the
terminal can change the colors.
Syntax ~
@@ -115,8 +129,8 @@ Syntax ~
If you want to use more options use the |term_start()|
function.
When the buffer associated with the terminal is wiped out the job is killed,
similar to calling `job_stop(job, "kill")`
When the buffer associated with the terminal is unloaded or wiped out the job
is killed, similar to calling `job_stop(job, "kill")`
By default the 'bufhidden' option of the buffer will be set to "hide".
So long as the job is running: If the window is closed the buffer becomes
@@ -130,7 +144,7 @@ done, use options like this: >
Note that the window will open at an unexpected moment, this will interrupt
what you are doing.
*E947*
*E947* *E948*
So long as the job is running, the buffer is considered modified and Vim
cannot be quit easily, see |abandon|.
@@ -187,6 +201,8 @@ In Terminal-Normal mode the statusline and window title show "(Terminal)". If
the job ends while in Terminal-Normal mode this changes to
"(Terminal-finished)".
It is not possible to enter Insert mode from Terminal-Job mode.
Unix ~
@@ -226,7 +242,10 @@ You can download them from the following page:
https://github.com/rprichard/winpty
Just put the files somewhere in your PATH.
Just put the files somewhere in your PATH. You can set the 'winptydll' option
to point to the right file, if needed. If you have both the 32-bit and 64-bit
version, rename to winpty32.dll and winpty64.dll to match the way Vim was
build.
==============================================================================
2. Remote testing *terminal-testing*

View File

@@ -1,7 +1,7 @@
" These commands create the option window.
"
" Maintainer: Bram Moolenaar <Bram@vim.org>
" Last Change: 2017 Aug 11
" Last Change: 2017 Aug 19
" If there already is an option window, jump to that one.
let buf = bufnr('option-window')
@@ -510,6 +510,10 @@ if has("terminal")
call append("$", "termkey\tkey that precedes Vim commands in a terminal window")
call append("$", "\t(local to window)")
call <SID>OptionL("tk")
if exists("&winptydll")
call append("$", "winptydll\tname of the winpty dynamic library")
call <SID>OptionG("winptydll", &winptydll)
endif
endif

View File

@@ -2274,6 +2274,7 @@ test_arglist \
test_utf8_comparisons \
test_viminfo \
test_vimscript \
test_virtualedit \
test_visual \
test_window_cmd \
test_window_id \

View File

@@ -473,8 +473,8 @@ close_buffer(
{
if (term_job_running(buf->b_term))
{
if (wipe_buf)
/* Wiping out a terminal buffer kills the job. */
if (wipe_buf || unload_buf)
/* Wiping out or unloading a terminal buffer kills the job. */
free_terminal(buf);
else
{
@@ -1648,7 +1648,7 @@ do_buffer(
if (bufIsChanged(curbuf))
#endif
{
EMSG(_(e_nowrtmsg));
no_write_message();
return FAIL;
}
}
@@ -1897,6 +1897,28 @@ do_autochdir(void)
}
#endif
void
no_write_message(void)
{
#ifdef FEAT_TERMINAL
if (term_job_running(curbuf->b_term))
EMSG(_("E948: Job still running (add ! to end the job)"));
else
#endif
EMSG(_("E37: No write since last change (add ! to override)"));
}
void
no_write_message_nobang(void)
{
#ifdef FEAT_TERMINAL
if (term_job_running(curbuf->b_term))
EMSG(_("E948: Job still running"));
else
#endif
EMSG(_("E37: No write since last change"));
}
/*
* functions for dealing with the buffer list
*/

View File

@@ -1373,7 +1373,7 @@ can_write_buf_line(channel_T *channel)
}
/*
* Write any lines to the input channel.
* Write any buffer lines to the input channel.
*/
static void
channel_write_in(channel_T *channel)
@@ -1445,6 +1445,25 @@ channel_buffer_free(buf_T *buf)
}
}
/*
* Write any lines waiting to be written to "channel".
*/
static void
channel_write_input(channel_T *channel)
{
chanpart_T *in_part = &channel->ch_part[PART_IN];
if (in_part->ch_writeque.wq_next != NULL)
channel_send(channel, PART_IN, (char_u *)"", 0, "channel_write_input");
else if (in_part->ch_bufref.br_buf != NULL)
{
if (in_part->ch_buf_append)
channel_write_new_lines(in_part->ch_bufref.br_buf);
else
channel_write_in(channel);
}
}
/*
* Write any lines waiting to be written to a channel.
*/
@@ -1454,17 +1473,7 @@ channel_write_any_lines(void)
channel_T *channel;
for (channel = first_channel; channel != NULL; channel = channel->ch_next)
{
chanpart_T *in_part = &channel->ch_part[PART_IN];
if (in_part->ch_bufref.br_buf != NULL)
{
if (in_part->ch_buf_append)
channel_write_new_lines(in_part->ch_bufref.br_buf);
else
channel_write_in(channel);
}
}
channel_write_input(channel);
}
/*
@@ -2984,7 +2993,9 @@ channel_fill_wfds(int maxfd_arg, fd_set *wfds)
{
chanpart_T *in_part = &ch->ch_part[PART_IN];
if (in_part->ch_fd != INVALID_FD && in_part->ch_bufref.br_buf != NULL)
if (in_part->ch_fd != INVALID_FD
&& (in_part->ch_bufref.br_buf != NULL
|| in_part->ch_writeque.wq_next != NULL))
{
FD_SET((int)in_part->ch_fd, wfds);
if ((int)in_part->ch_fd >= maxfd)
@@ -3007,7 +3018,9 @@ channel_fill_poll_write(int nfd_in, struct pollfd *fds)
{
chanpart_T *in_part = &ch->ch_part[PART_IN];
if (in_part->ch_fd != INVALID_FD && in_part->ch_bufref.br_buf != NULL)
if (in_part->ch_fd != INVALID_FD
&& (in_part->ch_bufref.br_buf != NULL
|| in_part->ch_writeque.wq_next != NULL))
{
in_part->ch_poll_idx = nfd;
fds[nfd].fd = in_part->ch_fd;
@@ -3529,6 +3542,29 @@ channel_handle_events(void)
}
# endif
/*
* Set "channel"/"part" to non-blocking.
* Only works for sockets and pipes.
*/
void
channel_set_nonblock(channel_T *channel, ch_part_T part)
{
chanpart_T *ch_part = &channel->ch_part[part];
int fd = ch_part->ch_fd;
if (fd != INVALID_FD)
{
#ifdef _WIN32
u_long val = 1;
ioctlsocket(fd, FIONBIO, &val);
#else
(void)fcntl(fd, F_SETFL, O_NONBLOCK);
#endif
ch_part->ch_nonblocking = TRUE;
}
}
/*
* Write "buf" (NUL terminated string) to "channel"/"part".
* When "fun" is not NULL an error message might be given.
@@ -3538,14 +3574,16 @@ channel_handle_events(void)
channel_send(
channel_T *channel,
ch_part_T part,
char_u *buf,
int len,
char_u *buf_arg,
int len_arg,
char *fun)
{
int res;
sock_T fd;
chanpart_T *ch_part = &channel->ch_part[part];
int did_use_queue = FALSE;
fd = channel->ch_part[part].ch_fd;
fd = ch_part->ch_fd;
if (fd == INVALID_FD)
{
if (!channel->ch_error && fun != NULL)
@@ -3561,29 +3599,144 @@ channel_send(
{
ch_log_lead("SEND ", channel);
fprintf(log_fd, "'");
ignored = (int)fwrite(buf, len, 1, log_fd);
ignored = (int)fwrite(buf_arg, len_arg, 1, log_fd);
fprintf(log_fd, "'\n");
fflush(log_fd);
did_log_msg = TRUE;
}
if (part == PART_SOCK)
res = sock_write(fd, (char *)buf, len);
else
res = fd_write(fd, (char *)buf, len);
if (res != len)
for (;;)
{
if (!channel->ch_error && fun != NULL)
{
ch_error(channel, "%s(): write failed", fun);
EMSG2(_("E631: %s(): write failed"), fun);
}
channel->ch_error = TRUE;
return FAIL;
}
writeq_T *wq = &ch_part->ch_writeque;
char_u *buf;
int len;
channel->ch_error = FALSE;
return OK;
if (wq->wq_next != NULL)
{
/* first write what was queued */
buf = wq->wq_next->wq_ga.ga_data;
len = wq->wq_next->wq_ga.ga_len;
did_use_queue = TRUE;
}
else
{
if (len_arg == 0)
/* nothing to write, called from channel_select_check() */
return OK;
buf = buf_arg;
len = len_arg;
}
if (part == PART_SOCK)
res = sock_write(fd, (char *)buf, len);
else
res = fd_write(fd, (char *)buf, len);
if (res < 0 && (errno == EWOULDBLOCK
#ifdef EAGAIN
|| errno == EAGAIN
#endif
))
res = 0; /* nothing got written */
if (res >= 0 && ch_part->ch_nonblocking)
{
writeq_T *entry = wq->wq_next;
if (did_use_queue)
ch_log(channel, "Sent %d bytes now", res);
if (res == len)
{
/* Wrote all the buf[len] bytes. */
if (entry != NULL)
{
/* Remove the entry from the write queue. */
ga_clear(&entry->wq_ga);
wq->wq_next = entry->wq_next;
if (wq->wq_next == NULL)
wq->wq_prev = NULL;
else
wq->wq_next->wq_prev = NULL;
continue;
}
if (did_use_queue)
ch_log(channel, "Write queue empty");
}
else
{
/* Wrote only buf[res] bytes, can't write more now. */
if (entry != NULL)
{
if (res > 0)
{
/* Remove the bytes that were written. */
mch_memmove(entry->wq_ga.ga_data,
(char *)entry->wq_ga.ga_data + res,
len - res);
entry->wq_ga.ga_len -= res;
}
buf = buf_arg;
len = len_arg;
}
else
{
buf += res;
len -= res;
}
ch_log(channel, "Adding %d bytes to the write queue", len);
/* Append the not written bytes of the argument to the write
* buffer. Limit entries to 4000 bytes. */
if (wq->wq_prev != NULL
&& wq->wq_prev->wq_ga.ga_len + len < 4000)
{
writeq_T *last = wq->wq_prev;
/* append to the last entry */
if (ga_grow(&last->wq_ga, len) == OK)
{
mch_memmove((char *)last->wq_ga.ga_data
+ last->wq_ga.ga_len,
buf, len);
last->wq_ga.ga_len += len;
}
}
else
{
writeq_T *last = (writeq_T *)alloc((int)sizeof(writeq_T));
if (last != NULL)
{
last->wq_prev = wq->wq_prev;
last->wq_next = NULL;
if (wq->wq_prev == NULL)
wq->wq_next = last;
else
wq->wq_prev->wq_next = last;
wq->wq_prev = last;
ga_init2(&last->wq_ga, 1, 1000);
if (ga_grow(&last->wq_ga, len) == OK)
{
mch_memmove(last->wq_ga.ga_data, buf, len);
last->wq_ga.ga_len = len;
}
}
}
}
}
else if (res != len)
{
if (!channel->ch_error && fun != NULL)
{
ch_error(channel, "%s(): write failed", fun);
EMSG2(_("E631: %s(): write failed"), fun);
}
channel->ch_error = TRUE;
return FAIL;
}
channel->ch_error = FALSE;
return OK;
}
}
/*
@@ -3795,13 +3948,7 @@ channel_poll_check(int ret_in, void *fds_in)
idx = in_part->ch_poll_idx;
if (ret > 0 && idx != -1 && (fds[idx].revents & POLLOUT))
{
if (in_part->ch_buf_append)
{
if (in_part->ch_bufref.br_buf != NULL)
channel_write_new_lines(in_part->ch_bufref.br_buf);
}
else
channel_write_in(channel);
channel_write_input(channel);
--ret;
}
}
@@ -3873,13 +4020,7 @@ channel_select_check(int ret_in, void *rfds_in, void *wfds_in)
if (ret > 0 && in_part->ch_fd != INVALID_FD
&& FD_ISSET(in_part->ch_fd, wfds))
{
if (in_part->ch_buf_append)
{
if (in_part->ch_bufref.br_buf != NULL)
channel_write_new_lines(in_part->ch_bufref.br_buf);
}
else
channel_write_in(channel);
channel_write_input(channel);
--ret;
}
}

View File

@@ -5926,7 +5926,7 @@ f_has(typval_T *argvars, typval_T *rettv)
#ifdef FEAT_TERMGUICOLORS
"termguicolors",
#endif
#ifdef FEAT_TERMINAL
#if defined(FEAT_TERMINAL) && !defined(WIN3264)
"terminal",
#endif
#ifdef TERMINFO
@@ -6133,6 +6133,10 @@ f_has(typval_T *argvars, typval_T *rettv)
#ifdef FEAT_NETBEANS_INTG
else if (STRICMP(name, "netbeans_enabled") == 0)
n = netbeans_active();
#endif
#if defined(FEAT_TERMINAL) && defined(WIN3264)
else if (STRICMP(name, "terminal") == 0)
n = terminal_enabled();
#endif
}

View File

@@ -3572,7 +3572,7 @@ getfile(
{
if (other)
--no_wait_return;
EMSG(_(e_nowrtmsg));
no_write_message();
retval = GETFILE_NOT_WRITTEN; /* file has been changed */
goto theend;
}

View File

@@ -1934,9 +1934,9 @@ check_changed(buf_T *buf, int flags)
}
#endif
if (flags & CCGD_EXCMD)
EMSG(_(e_nowrtmsg));
no_write_message();
else
EMSG(_(e_nowrtmsg_nobang));
no_write_message_nobang();
return TRUE;
}
return FALSE;

View File

@@ -7468,7 +7468,7 @@ ex_win_close(
else
# endif
{
EMSG(_(e_nowrtmsg));
no_write_message();
return;
}
}

View File

@@ -1516,8 +1516,6 @@ EXTERN char_u e_notcreate[] INIT(= N_("E482: Can't create file %s"));
EXTERN char_u e_notmp[] INIT(= N_("E483: Can't get temp file name"));
EXTERN char_u e_notopen[] INIT(= N_("E484: Can't open file %s"));
EXTERN char_u e_notread[] INIT(= N_("E485: Can't read file %s"));
EXTERN char_u e_nowrtmsg[] INIT(= N_("E37: No write since last change (add ! to override)"));
EXTERN char_u e_nowrtmsg_nobang[] INIT(= N_("E37: No write since last change"));
EXTERN char_u e_null[] INIT(= N_("E38: Null argument"));
#if defined(FEAT_DIGRAPHS) || defined(FEAT_TIMERS)
EXTERN char_u e_number_exp[] INIT(= N_("E39: Number expected"));

View File

@@ -3597,36 +3597,35 @@ set_progpath(char_u *argv0)
{
char_u *val = argv0;
# ifdef PROC_EXE_LINK
char buf[MAXPATHL + 1];
ssize_t len;
len = readlink(PROC_EXE_LINK, buf, MAXPATHL);
if (len > 0)
{
buf[len] = NUL;
val = (char_u *)buf;
}
# else
# if defined(WIN32)
/* A relative path containing a "/" will become invalid when using ":cd",
* turn it into a full path.
* On MS-Windows "vim" should be expanded to "vim.exe", thus always do
* this. */
# ifdef WIN32
char_u *path = NULL;
if (mch_can_exe(argv0, &path, FALSE) && path != NULL)
val = path;
# else
char_u buf[MAXPATHL];
# else
char_u buf[MAXPATHL + 1];
# ifdef PROC_EXE_LINK
char linkbuf[MAXPATHL + 1];
ssize_t len;
if (!mch_isFullName(argv0))
len = readlink(PROC_EXE_LINK, linkbuf, MAXPATHL);
if (len > 0)
{
if (gettail(argv0) != argv0
&& vim_FullName(argv0, buf, MAXPATHL, TRUE) != FAIL)
val = buf;
linkbuf[len] = NUL;
val = (char_u *)linkbuf;
}
# endif
if (!mch_isFullName(val))
{
if (gettail(val) != val
&& vim_FullName(val, buf, MAXPATHL, TRUE) != FAIL)
val = buf;
}
# endif
set_vim_var_string(VV_PROGPATH, val, -1);

View File

@@ -2313,7 +2313,7 @@ msg_scroll_up(void)
gui_undraw_cursor();
#endif
/* scrolling up always works */
screen_del_lines(0, 0, 1, (int)Rows, TRUE, NULL);
screen_del_lines(0, 0, 1, (int)Rows, TRUE, 0, NULL);
if (!can_clear((char_u *)" "))
{
@@ -2905,7 +2905,7 @@ do_more_prompt(int typed_char)
}
if (toscroll == -1 && screen_ins_lines(0, 0, 1,
(int)Rows, NULL) == OK)
(int)Rows, 0, NULL) == OK)
{
/* display line at top */
(void)disp_sb_line(0, mp);

View File

@@ -605,7 +605,18 @@ check_cursor_col_win(win_T *win)
else if (ve_flags == VE_ALL)
{
if (oldcoladd > win->w_cursor.col)
{
win->w_cursor.coladd = oldcoladd - win->w_cursor.col;
if (win->w_cursor.col < len && win->w_cursor.coladd > 0)
{
int cs, ce;
/* check that coladd is not more than the char width */
getvcol(win, &win->w_cursor, &cs, NULL, &ce);
if (win->w_cursor.coladd > ce - cs)
win->w_cursor.coladd = ce - cs;
}
}
else
/* avoid weird number when there is a miscalculation or overflow */
win->w_cursor.coladd = 0;

View File

@@ -1163,7 +1163,7 @@ curs_columns(
if (extra > 0)
win_ins_lines(curwin, 0, extra, FALSE, FALSE);
else if (extra < 0)
win_del_lines(curwin, 0, -extra, FALSE, FALSE);
win_del_lines(curwin, 0, -extra, FALSE, FALSE, 0);
}
else
curwin->w_skipcol = 0;

View File

@@ -1571,7 +1571,12 @@ do_pending_operator(cmdarg_T *cap, int old_col, int gui_yank)
oap->start = VIsual;
if (VIsual_mode == 'V')
{
oap->start.col = 0;
# ifdef FEAT_VIRTUALEDIT
oap->start.coladd = 0;
# endif
}
}
/*
@@ -7580,6 +7585,7 @@ nv_gomark(cmdarg_T *cap)
if (!virtual_active())
curwin->w_cursor.coladd = 0;
#endif
check_cursor_col();
#ifdef FEAT_FOLDING
if (cap->oap->op_type == OP_NOP
&& pos != NULL

View File

@@ -3179,6 +3179,7 @@ static struct vimoption options[] =
p_term("t_SC", T_CSC)
p_term("t_EC", T_CEC)
p_term("t_SH", T_CSH)
p_term("t_RS", T_CRS)
p_term("t_IS", T_CIS)
p_term("t_ke", T_KE)
p_term("t_ks", T_KS)

View File

@@ -13,6 +13,8 @@ int do_buffer(int action, int start, int dir, int count, int forceit);
void set_curbuf(buf_T *buf, int action);
void enter_buffer(buf_T *buf);
void do_autochdir(void);
void no_write_message(void);
void no_write_message_nobang(void);
buf_T *buflist_new(char_u *ffname, char_u *sfname, linenr_T lnum, int flags);
void free_buf_options(buf_T *buf, int free_p_ff);
int buflist_getfile(int n, linenr_T lnum, int options, int forceit);

View File

@@ -35,6 +35,7 @@ char_u *channel_read_block(channel_T *channel, ch_part_T part, int timeout);
void common_channel_read(typval_T *argvars, typval_T *rettv, int raw);
channel_T *channel_fd2channel(sock_T fd, ch_part_T *partp);
void channel_handle_events(void);
void channel_set_nonblock(channel_T *channel, ch_part_T part);
int channel_send(channel_T *channel, ch_part_T part, char_u *buf, int len, char *fun);
void ch_expr_common(typval_T *argvars, typval_T *rettv, int eval);
void ch_raw_common(typval_T *argvars, typval_T *rettv, int eval);

View File

@@ -45,9 +45,9 @@ void screen_start(void);
void windgoto(int row, int col);
void setcursor(void);
int win_ins_lines(win_T *wp, int row, int line_count, int invalid, int mayclear);
int win_del_lines(win_T *wp, int row, int line_count, int invalid, int mayclear);
int screen_ins_lines(int off, int row, int line_count, int end, win_T *wp);
int screen_del_lines(int off, int row, int line_count, int end, int force, win_T *wp);
int win_del_lines(win_T *wp, int row, int line_count, int invalid, int mayclear, int clear_attr);
int screen_ins_lines(int off, int row, int line_count, int end, int clear_attr, win_T *wp);
int screen_del_lines(int off, int row, int line_count, int end, int force, int attr, win_T *wp);
int showmode(void);
void unshowmode(int force);
void clearmode(void);

View File

@@ -34,4 +34,5 @@ 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);
int terminal_enabled(void);
/* vim: set ft=c : */

View File

@@ -2327,7 +2327,7 @@ win_found:
* set b_p_ro flag). */
if (!can_abandon(curbuf, forceit))
{
EMSG(_(e_nowrtmsg));
no_write_message();
ok = FALSE;
}
else

View File

@@ -151,13 +151,13 @@ static void screen_char(unsigned off, int row, int col);
static void screen_char_2(unsigned off, int row, int col);
#endif
static void screenclear2(void);
static void lineclear(unsigned off, int width);
static void lineclear(unsigned off, int width, int attr);
static void lineinvalid(unsigned off, int width);
#ifdef FEAT_WINDOWS
static void linecopy(int to, int from, win_T *wp);
static void redraw_block(int row, int end, win_T *wp);
#endif
static int win_do_lines(win_T *wp, int row, int line_count, int mayclear, int del);
static int win_do_lines(win_T *wp, int row, int line_count, int mayclear, int del, int clear_attr);
static void win_rest_invalid(win_T *wp);
static void msg_pos_mode(void);
static void recording_mode(int attr);
@@ -609,7 +609,8 @@ update_screen(int type_arg)
else if (type != CLEAR)
{
check_for_delay(FALSE);
if (screen_ins_lines(0, 0, msg_scrolled, (int)Rows, NULL) == FAIL)
if (screen_ins_lines(0, 0, msg_scrolled, (int)Rows, 0, NULL)
== FAIL)
type = CLEAR;
FOR_ALL_WINDOWS(wp)
{
@@ -1537,7 +1538,8 @@ win_update(win_T *wp)
if (row > 0)
{
check_for_delay(FALSE);
if (win_del_lines(wp, 0, row, FALSE, wp == firstwin) == OK)
if (win_del_lines(wp, 0, row, FALSE, wp == firstwin, 0)
== OK)
bot_start = wp->w_height - row;
else
mid_start = 0; /* redraw all lines */
@@ -2003,7 +2005,7 @@ win_update(win_T *wp)
{
check_for_delay(FALSE);
if (win_del_lines(wp, row,
-xtra_rows, FALSE, FALSE) == FAIL)
-xtra_rows, FALSE, FALSE, 0) == FAIL)
mod_bot = MAXLNUM;
else
bot_start = wp->w_height + xtra_rows;
@@ -6773,7 +6775,7 @@ win_redr_status_matches(
* no room, scroll the screen one line up. */
if (cmdline_row == Rows - 1)
{
screen_del_lines(0, 0, 1, (int)Rows, TRUE, NULL);
screen_del_lines(0, 0, 1, (int)Rows, TRUE, 0, NULL);
++msg_scrolled;
}
else
@@ -9074,7 +9076,7 @@ screenclear2(void)
/* blank out ScreenLines */
for (i = 0; i < Rows; ++i)
{
lineclear(LineOffset[i], (int)Columns);
lineclear(LineOffset[i], (int)Columns, 0);
LineWraps[i] = FALSE;
}
@@ -9114,7 +9116,7 @@ screenclear2(void)
* Clear one line in ScreenLines.
*/
static void
lineclear(unsigned off, int width)
lineclear(unsigned off, int width, int attr)
{
(void)vim_memset(ScreenLines + off, ' ', (size_t)width * sizeof(schar_T));
#ifdef FEAT_MBYTE
@@ -9122,7 +9124,7 @@ lineclear(unsigned off, int width)
(void)vim_memset(ScreenLinesUC + off, 0,
(size_t)width * sizeof(u8char_T));
#endif
(void)vim_memset(ScreenAttrs + off, 0, (size_t)width * sizeof(sattr_T));
(void)vim_memset(ScreenAttrs + off, attr, (size_t)width * sizeof(sattr_T));
}
/*
@@ -9508,7 +9510,7 @@ win_ins_lines(
if (line_count > wp->w_height - row)
line_count = wp->w_height - row;
retval = win_do_lines(wp, row, line_count, mayclear, FALSE);
retval = win_do_lines(wp, row, line_count, mayclear, FALSE, 0);
if (retval != MAYBE)
return retval;
@@ -9523,7 +9525,7 @@ win_ins_lines(
if (wp->w_next != NULL || wp->w_status_height)
{
if (screen_del_lines(0, W_WINROW(wp) + wp->w_height - line_count,
line_count, (int)Rows, FALSE, NULL) == OK)
line_count, (int)Rows, FALSE, 0, NULL) == OK)
did_delete = TRUE;
else if (wp->w_next)
return FAIL;
@@ -9547,7 +9549,7 @@ win_ins_lines(
' ', ' ', 0);
}
if (screen_ins_lines(0, W_WINROW(wp) + row, line_count, (int)Rows, NULL)
if (screen_ins_lines(0, W_WINROW(wp) + row, line_count, (int)Rows, 0, NULL)
== FAIL)
{
/* deletion will have messed up other windows */
@@ -9577,7 +9579,8 @@ win_del_lines(
int row,
int line_count,
int invalid,
int mayclear)
int mayclear,
int clear_attr) /* for clearing lines */
{
int retval;
@@ -9587,12 +9590,12 @@ win_del_lines(
if (line_count > wp->w_height - row)
line_count = wp->w_height - row;
retval = win_do_lines(wp, row, line_count, mayclear, TRUE);
retval = win_do_lines(wp, row, line_count, mayclear, TRUE, clear_attr);
if (retval != MAYBE)
return retval;
if (screen_del_lines(0, W_WINROW(wp) + row, line_count,
(int)Rows, FALSE, NULL) == FAIL)
(int)Rows, FALSE, clear_attr, NULL) == FAIL)
return FAIL;
#ifdef FEAT_WINDOWS
@@ -9603,7 +9606,7 @@ win_del_lines(
if (wp->w_next || wp->w_status_height || cmdline_row < Rows - 1)
{
if (screen_ins_lines(0, W_WINROW(wp) + wp->w_height - line_count,
line_count, (int)Rows, NULL) == FAIL)
line_count, (int)Rows, clear_attr, NULL) == FAIL)
{
wp->w_redr_status = TRUE;
win_rest_invalid(wp->w_next);
@@ -9630,7 +9633,8 @@ win_do_lines(
int row,
int line_count,
int mayclear,
int del)
int del,
int clear_attr)
{
int retval;
@@ -9694,10 +9698,10 @@ win_do_lines(
scroll_region_set(wp, row);
if (del)
retval = screen_del_lines(W_WINROW(wp) + row, 0, line_count,
wp->w_height - row, FALSE, wp);
wp->w_height - row, FALSE, clear_attr, wp);
else
retval = screen_ins_lines(W_WINROW(wp) + row, 0, line_count,
wp->w_height - row, wp);
wp->w_height - row, clear_attr, wp);
#ifdef FEAT_WINDOWS
if (scroll_region && (wp->w_width == Columns || *T_CSV != NUL))
#endif
@@ -9771,6 +9775,7 @@ screen_ins_lines(
int row,
int line_count,
int end,
int clear_attr,
win_T *wp) /* NULL or window to use width from */
{
int i;
@@ -9851,7 +9856,7 @@ screen_ins_lines(
*/
if (type == USE_T_CD || type == USE_T_CDL ||
type == USE_T_CE || type == USE_T_DL)
return screen_del_lines(off, row, line_count, end, FALSE, wp);
return screen_del_lines(off, row, line_count, end, FALSE, 0, wp);
/*
* If text is retained below the screen, first clear or delete as many
@@ -9859,7 +9864,7 @@ screen_ins_lines(
* the deleted lines won't later surface during a screen_del_lines.
*/
if (*T_DB)
screen_del_lines(off, end - line_count, line_count, end, FALSE, wp);
screen_del_lines(off, end - line_count, line_count, end, FALSE, 0, wp);
#ifdef FEAT_CLIPBOARD
/* Remove a modeless selection when inserting lines halfway the screen
@@ -9902,7 +9907,8 @@ screen_ins_lines(
linecopy(j + line_count, j, wp);
j += line_count;
if (can_clear((char_u *)" "))
lineclear(LineOffset[j] + wp->w_wincol, wp->w_width);
lineclear(LineOffset[j] + wp->w_wincol, wp->w_width,
clear_attr);
else
lineinvalid(LineOffset[j] + wp->w_wincol, wp->w_width);
LineWraps[j] = FALSE;
@@ -9920,7 +9926,7 @@ screen_ins_lines(
LineOffset[j + line_count] = temp;
LineWraps[j + line_count] = FALSE;
if (can_clear((char_u *)" "))
lineclear(temp, (int)Columns);
lineclear(temp, (int)Columns, clear_attr);
else
lineinvalid(temp, (int)Columns);
}
@@ -9928,6 +9934,8 @@ screen_ins_lines(
screen_stop_highlight();
windgoto(cursor_row, 0);
if (clear_attr != 0)
screen_start_highlight(clear_attr);
#ifdef FEAT_WINDOWS
/* redraw the characters */
@@ -9993,6 +10001,7 @@ screen_del_lines(
int line_count,
int end,
int force, /* even when line_count > p_ttyscroll */
int clear_attr, /* used for clearing lines */
win_T *wp UNUSED) /* NULL or window to use width from */
{
int j;
@@ -10136,7 +10145,8 @@ screen_del_lines(
linecopy(j - line_count, j, wp);
j -= line_count;
if (can_clear((char_u *)" "))
lineclear(LineOffset[j] + wp->w_wincol, wp->w_width);
lineclear(LineOffset[j] + wp->w_wincol, wp->w_width,
clear_attr);
else
lineinvalid(LineOffset[j] + wp->w_wincol, wp->w_width);
LineWraps[j] = FALSE;
@@ -10155,13 +10165,16 @@ screen_del_lines(
LineOffset[j - line_count] = temp;
LineWraps[j - line_count] = FALSE;
if (can_clear((char_u *)" "))
lineclear(temp, (int)Columns);
lineclear(temp, (int)Columns, clear_attr);
else
lineinvalid(temp, (int)Columns);
}
}
screen_stop_highlight();
if (screen_attr != clear_attr)
screen_stop_highlight();
if (clear_attr != 0)
screen_start_highlight(clear_attr);
#ifdef FEAT_WINDOWS
/* redraw the characters */

View File

@@ -1196,6 +1196,7 @@ typedef struct partial_S partial_T;
typedef struct jobvar_S job_T;
typedef struct readq_S readq_T;
typedef struct writeq_S writeq_T;
typedef struct jsonq_S jsonq_T;
typedef struct cbq_S cbq_T;
typedef struct channel_S channel_T;
@@ -1512,6 +1513,13 @@ struct readq_S
readq_T *rq_prev;
};
struct writeq_S
{
garray_T wq_ga;
writeq_T *wq_next;
writeq_T *wq_prev;
};
struct jsonq_S
{
typval_T *jq_value;
@@ -1601,6 +1609,8 @@ typedef struct {
#endif
int ch_block_write; /* for testing: 0 when not used, -1 when write
* does not block, 1 simulate blocking */
int ch_nonblocking; /* write() is non-blocking */
writeq_T ch_writeque; /* header for write queue */
cbq_T ch_cb_head; /* dummy node for per-request callbacks */
char_u *ch_callback; /* call when a msg is not handled */

View File

@@ -9999,7 +9999,7 @@ highlight_changed(void)
if (ga_grow(&highlight_ga, 28) == FAIL)
return FAIL;
hlcnt = highlight_ga.ga_len;
if (id_S == 0)
if (id_S == -1)
{
/* Make sure id_S is always valid to simplify code below. Use the last
* entry. */

View File

@@ -114,21 +114,22 @@ char *tgetstr(char *, char **);
# else
# define LOG_TR(msg)
# endif
# define STATUS_GET 1 /* send request when switching to RAW mode */
# define STATUS_SENT 2 /* did send request, waiting for response */
# define STATUS_GOT 3 /* received response */
/* Request Terminal Version status: */
# define CRV_GET 1 /* send T_CRV when switched to RAW mode */
# define CRV_SENT 2 /* did send T_CRV, waiting for answer */
# define CRV_GOT 3 /* received T_CRV response */
static int crv_status = CRV_GET;
static int crv_status = STATUS_GET;
/* Request Cursor position report: */
# define U7_GET 1 /* send T_U7 when switched to RAW mode */
# define U7_SENT 2 /* did send T_U7, waiting for answer */
# define U7_GOT 3 /* received T_U7 response */
static int u7_status = U7_GET;
static int u7_status = STATUS_GET;
/* Request background color report: */
# define RBG_GET 1 /* send T_RBG when switched to RAW mode */
# define RBG_SENT 2 /* did send T_RBG, waiting for answer */
# define RBG_GOT 3 /* received T_RBG response */
static int rbg_status = RBG_GET;
static int rbg_status = STATUS_GET;
/* Request cursor mode report: */
static int rcm_status = STATUS_GET;
# endif
/*
@@ -160,6 +161,12 @@ static char_u *vim_tgetstr(char *s, char_u **pp);
static int detected_8bit = FALSE; /* detected 8-bit terminal */
/* When the cursor shape was detected these values are used:
* 1: block, 2: underline, 3: vertical bar
* initial_cursor_blink is only valid when initial_cursor_shape is not zero. */
static int initial_cursor_shape = 0;
static int initial_cursor_blink = FALSE;
static struct builtin_term builtin_termcaps[] =
{
@@ -819,12 +826,18 @@ static struct builtin_term builtin_termcaps[] =
{(int)KS_LE, "\b"},
{(int)KS_VI, IF_EB("\033[?25l", ESC_STR "[?25l")},
{(int)KS_VE, IF_EB("\033[?25h", ESC_STR "[?25h")},
#if 0
/* This is currently disabled, because we cannot reliably restore the
* cursor style because of what appears to be an xterm bug. */
{(int)KS_VE, IF_EB("\033[?25h\033[?12l", ESC_STR "[?25h" ESC_STR "[?12l")},
{(int)KS_VS, IF_EB("\033[?12h", ESC_STR "[?12h")},
# ifdef TERMINFO
{(int)KS_CSH, IF_EB("\033[%p1%d q", ESC_STR "[%p1%d q")},
# else
{(int)KS_CSH, IF_EB("\033[%d q", ESC_STR "[%d q")},
# endif
#endif
{(int)KS_CRS, IF_EB("\033P$q q\033\\", ESC_STR "P$q q" ESC_STR "\\")},
# ifdef TERMINFO
{(int)KS_CM, IF_EB("\033[%i%p1%d;%p2%dH",
ESC_STR "[%i%p1%d;%p2%dH")},
@@ -1803,9 +1816,9 @@ set_termname(char_u *term)
* is being used.
* Don't do this when the GUI is active, it uses "t_kb" and "t_kD" directly.
*/
#ifdef FEAT_GUI
# ifdef FEAT_GUI
if (!gui.in_use)
#endif
# endif
get_stty();
#endif
@@ -1906,8 +1919,8 @@ set_termname(char_u *term)
full_screen = TRUE; /* we can use termcap codes from now on */
set_term_defaults(); /* use current values as defaults */
#ifdef FEAT_TERMRESPONSE
LOG_TR("setting crv_status to CRV_GET");
crv_status = CRV_GET; /* Get terminal version later */
LOG_TR("setting crv_status to STATUS_GET");
crv_status = STATUS_GET; /* Get terminal version later */
#endif
/*
@@ -3298,9 +3311,10 @@ settmode(int tmode)
/* May need to check for T_CRV response and termcodes, it
* doesn't work in Cooked mode, an external program may get
* them. */
if (tmode != TMODE_RAW && (crv_status == CRV_SENT
|| u7_status == U7_SENT
|| rbg_status == RBG_SENT))
if (tmode != TMODE_RAW && (crv_status == STATUS_SENT
|| u7_status == STATUS_SENT
|| rbg_status == STATUS_SENT
|| rcm_status == STATUS_SENT))
(void)vpeekc_nomap();
check_for_codes_from_term();
}
@@ -3347,7 +3361,7 @@ starttermcap(void)
may_req_termresponse();
/* Immediately check for a response. If t_Co changes, we don't
* want to redraw with wrong colors first. */
if (crv_status == CRV_SENT)
if (crv_status == STATUS_SENT)
check_for_codes_from_term();
}
#endif
@@ -3367,8 +3381,10 @@ stoptermcap(void)
# endif
{
/* May need to discard T_CRV, T_U7 or T_RBG response. */
if (crv_status == CRV_SENT || u7_status == U7_SENT
|| rbg_status == RBG_SENT)
if (crv_status == STATUS_SENT
|| u7_status == STATUS_SENT
|| rbg_status == STATUS_SENT
|| rcm_status == STATUS_SENT)
{
# ifdef UNIX
/* Give the terminal a chance to respond. */
@@ -3414,14 +3430,14 @@ stoptermcap(void)
void
may_req_termresponse(void)
{
if (crv_status == CRV_GET
if (crv_status == STATUS_GET
&& can_get_termresponse()
&& starting == 0
&& *T_CRV != NUL)
{
LOG_TR("Sending CRV");
LOG_TR("Sending CRV request");
out_str(T_CRV);
crv_status = CRV_SENT;
crv_status = STATUS_SENT;
/* check for the characters now, otherwise they might be eaten by
* get_keystroke() */
out_flush();
@@ -3442,7 +3458,7 @@ may_req_termresponse(void)
void
may_req_ambiguous_char_width(void)
{
if (u7_status == U7_GET
if (u7_status == STATUS_GET
&& can_get_termresponse()
&& starting == 0
&& *T_U7 != NUL
@@ -3457,7 +3473,7 @@ may_req_ambiguous_char_width(void)
buf[mb_char2bytes(0x25bd, buf)] = 0;
out_str(buf);
out_str(T_U7);
u7_status = U7_SENT;
u7_status = STATUS_SENT;
out_flush();
/* This overwrites a few characters on the screen, a redraw is needed
@@ -3477,19 +3493,40 @@ may_req_ambiguous_char_width(void)
/*
* Similar to requesting the version string: Request the terminal background
* color when it is the right moment.
* Also request the cursor shape, if possible.
*/
void
may_req_bg_color(void)
{
if (rbg_status == RBG_GET
&& can_get_termresponse()
&& starting == 0
&& *T_RBG != NUL
&& !option_was_set((char_u *)"bg"))
int done = FALSE;
if (can_get_termresponse() && starting == 0)
{
/* Only request background if t_RB is set and 'background' wasn't
* changed. */
if (rbg_status == STATUS_GET
&& *T_RBG != NUL
&& !option_was_set((char_u *)"bg"))
{
LOG_TR("Sending BG request");
out_str(T_RBG);
rbg_status = STATUS_SENT;
done = TRUE;
}
/* Only request the cursor shape if t_SH and t_RS are set. */
if (rcm_status == STATUS_GET
&& *T_CSH != NUL
&& *T_CRS != NUL)
{
LOG_TR("Sending cursor shape request");
out_str(T_CRS);
rcm_status = STATUS_SENT;
done = TRUE;
}
}
if (done)
{
LOG_TR("Sending BG request");
out_str(T_RBG);
rbg_status = RBG_SENT;
/* check for the characters now, otherwise they might be eaten by
* get_keystroke() */
out_flush();
@@ -3676,7 +3713,12 @@ term_cursor_mode(int forced)
/* Only do something when redrawing the screen and we can restore the
* mode. */
if (!full_screen || *T_CEI == NUL)
{
if (forced && initial_cursor_shape > 0)
/* Restore to initial values. */
term_cursor_shape(initial_cursor_shape, initial_cursor_blink);
return;
}
if ((State & REPLACE) == REPLACE)
{
@@ -3730,6 +3772,7 @@ term_cursor_blink(int blink)
out_str(T_VE);
out_flush();
}
# endif
/*
* "shape" == 1: block, "shape" == 2: underline, "shape" == 3: vertical bar
@@ -3743,7 +3786,6 @@ term_cursor_shape(int shape, int blink)
out_flush();
}
}
# endif
#endif
/*
@@ -4290,7 +4332,8 @@ check_termcode(
{
/* Skip over the digits, the final char must
* follow. */
for (j = slen - 2; j < len && (isdigit(tp[j]) || tp[j] == ';'); ++j)
for (j = slen - 2; j < len && (isdigit(tp[j])
|| tp[j] == ';'); ++j)
;
++j;
if (len < j) /* got a partial sequence */
@@ -4394,7 +4437,7 @@ check_termcode(
char *aw = NULL;
LOG_TR("Received U7 status");
u7_status = U7_GOT;
u7_status = STATUS_GOT;
# ifdef FEAT_AUTOCMD
did_cursorhold = TRUE;
# endif
@@ -4433,8 +4476,8 @@ check_termcode(
/* eat it when at least one digit and ending in 'c' */
if (*T_CRV != NUL && i > 2 + (tp[0] != CSI) && tp[i] == 'c')
{
LOG_TR("Received CRV");
crv_status = CRV_GOT;
LOG_TR("Received CRV response");
crv_status = STATUS_GOT;
# ifdef FEAT_AUTOCMD
did_cursorhold = TRUE;
# endif
@@ -4566,8 +4609,8 @@ check_termcode(
char *newval = (3 * '6' < tp[j+7] + tp[j+12]
+ tp[j+17]) ? "light" : "dark";
LOG_TR("Received RBG");
rbg_status = RBG_GOT;
LOG_TR("Received RBG response");
rbg_status = STATUS_GOT;
if (STRCMP(p_bg, newval) != 0)
{
/* value differs, apply it */
@@ -4592,24 +4635,33 @@ check_termcode(
}
/* Check for key code response from xterm:
*
* {lead}{flag}+r<hex bytes><{tail}
*
* {lead} can be <Esc>P or DCS
* {flag} can be '0' or '1'
* {tail} can be Esc>\ or STERM
*
* Consume any code that starts with "{lead}.+r".
* Check for cursor shape response from xterm:
* {lead}1$r<number> q{tail}
*
* {lead} can be <Esc>P or DCS
* {tail} can be Esc>\ or STERM
*
* Consume any code that starts with "{lead}.+r" or "{lead}.$r".
*/
else if (check_for_codes
else if ((check_for_codes || rcm_status == STATUS_SENT)
&& ((tp[0] == ESC && len >= 2 && tp[1] == 'P')
|| tp[0] == DCS))
{
j = 1 + (tp[0] == ESC);
if (len >= j + 3 && (argp[1] != '+' || argp[2] != 'r'))
if (len < j + 3)
i = len; /* need more chars */
else if ((argp[1] != '+' && argp[1] != '$') || argp[2] != 'r')
i = 0; /* no match */
else
else if (argp[1] == '+')
/* key code response */
for (i = j; i < len; ++i)
{
if ((tp[i] == ESC && i + 1 < len && tp[i + 1] == '\\')
|| tp[i] == STERM)
{
@@ -4620,6 +4672,32 @@ check_termcode(
slen = i + 1 + (tp[i] == ESC);
break;
}
}
else if ((len >= j + 6 && isdigit(argp[3]))
&& argp[4] == ' '
&& argp[5] == 'q')
{
/* cursor shape response */
i = j + 6;
if ((tp[i] == ESC && i + 1 < len && tp[i + 1] == '\\')
|| tp[i] == STERM)
{
int number = argp[3] - '0';
/* 0, 1 = block blink, 2 = block
* 3 = underline blink, 4 = underline
* 5 = vertical bar blink, 6 = vertical bar */
number = number == 0 ? 1 : number;
initial_cursor_shape = (number + 1) / 2;
initial_cursor_blink = (number & 1) ? TRUE : FALSE;
rcm_status = STATUS_GOT;
LOG_TR("Received cursor shape response");
key_name[0] = (int)KS_EXTRA;
key_name[1] = (int)KE_IGNORE;
slen = i + 1 + (tp[i] == ESC);
}
}
if (i == len)
{
@@ -5837,7 +5915,7 @@ gather_termleader(void)
termleader[len++] = CSI; /* the GUI codes are not in termcodes[] */
#endif
#ifdef FEAT_TERMRESPONSE
if (check_for_codes)
if (check_for_codes || *T_CRS != NUL)
termleader[len++] = DCS; /* the termcode response starts with DCS
in 8-bit mode */
#endif

View File

@@ -41,6 +41,7 @@ enum SpecialKey
KS_VE, /* cursor visible */
KS_VS, /* cursor very visible */
KS_CSH, /* cursor shape */
KS_CRS, /* request cursor shape */
KS_ME, /* normal mode */
KS_MR, /* reverse mode */
KS_MD, /* bold mode */
@@ -132,6 +133,7 @@ extern char_u *(term_strings[]); /* current terminal strings */
#define T_VE (TERM_STR(KS_VE)) /* cursor visible */
#define T_VS (TERM_STR(KS_VS)) /* cursor very visible */
#define T_CSH (TERM_STR(KS_CSH)) /* cursor shape */
#define T_CRS (TERM_STR(KS_CRS)) /* request cursor shape */
#define T_ME (TERM_STR(KS_ME)) /* normal mode */
#define T_MR (TERM_STR(KS_MR)) /* reverse mode */
#define T_MD (TERM_STR(KS_MD)) /* bold mode */

View File

@@ -38,6 +38,7 @@
* in tl_scrollback are no longer used.
*
* TODO:
* - help index for winptydll, optwin entry for winptydll
* - make [range]terminal pipe [range] lines to the terminal
* - implement term_setsize()
* - add test for giving error for invalid 'termsize' value.
@@ -267,7 +268,7 @@ term_start(typval_T *argvar, jobopt_T *opt, int forceit)
/* Create a new buffer in the current window. */
if (!can_abandon(curbuf, forceit))
{
EMSG(_(e_nowrtmsg));
no_write_message();
vim_free(term);
return;
}
@@ -400,6 +401,10 @@ term_start(typval_T *argvar, jobopt_T *opt, int forceit)
vterm_get_size(term->tl_vterm, &term->tl_rows, &term->tl_cols);
term_report_winsize(term, term->tl_rows, term->tl_cols);
/* Make sure we don't get stuck on sending keys to the job, it leads to
* a deadlock if the job is waiting for Vim to read. */
channel_set_nonblock(term->tl_job->jv_channel, PART_IN);
if (old_curbuf != NULL)
{
--curbuf->b_nwindows;
@@ -1032,14 +1037,13 @@ term_enter_job_mode()
/* 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)
while (curbuf->b_ml.ml_line_count > term->tl_scrollback_scrolled
&& gap->ga_len > 0)
{
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();
@@ -1283,9 +1287,10 @@ may_restore_cursor_props(void)
if (did_change_cursor)
{
did_change_cursor = FALSE;
ui_cursor_shape_forced(TRUE);
term_cursor_color((char_u *)"");
term_cursor_blink(FALSE);
/* this will restore the initial cursor style, if possible */
ui_cursor_shape_forced(TRUE);
}
}
@@ -1363,9 +1368,6 @@ terminal_loop(void)
if (c == K_IGNORE)
continue;
#ifdef UNIX
may_send_sigint(c, curbuf->b_term->tl_job->jv_pid, 0);
#endif
#ifdef WIN3264
/* On Windows winpty handles CTRL-C, don't send a CTRL_C_EVENT.
* Use CTRL-BREAK to kill the job. */
@@ -1401,6 +1403,11 @@ terminal_loop(void)
/* Send both keys to the terminal. */
send_keys_to_term(curbuf->b_term, prev_c, TRUE);
}
else if (c == Ctrl_C)
{
/* "CTRL-W CTRL-C" or 'termkey' CTRL-C: end the job */
mch_signal_job(curbuf->b_term->tl_job, (char_u *)"kill");
}
else if (termkey == 0 && c == '.')
{
/* "CTRL-W .": send CTRL-W to the job */
@@ -1483,6 +1490,169 @@ may_toggle_cursor(term_T *term)
}
}
/*
* Reverse engineer the RGB value into a cterm color index.
* First color is 1. Return 0 if no match found.
*/
static int
color2index(VTermColor *color, int fg, int *boldp)
{
int red = color->red;
int blue = color->blue;
int green = color->green;
/* The argument for lookup_color() is for the color_names[] table. */
if (red == 0)
{
if (green == 0)
{
if (blue == 0)
return lookup_color(0, fg, boldp) + 1; /* black */
if (blue == 224)
return lookup_color(1, fg, boldp) + 1; /* dark blue */
}
else if (green == 224)
{
if (blue == 0)
return lookup_color(2, fg, boldp) + 1; /* dark green */
if (blue == 224)
return lookup_color(3, fg, boldp) + 1; /* dark cyan */
}
}
else if (red == 224)
{
if (green == 0)
{
if (blue == 0)
return lookup_color(4, fg, boldp) + 1; /* dark red */
if (blue == 224)
return lookup_color(5, fg, boldp) + 1; /* dark magenta */
}
else if (green == 224)
{
if (blue == 0)
return lookup_color(6, fg, boldp) + 1; /* dark yellow / brown */
if (blue == 224)
return lookup_color(8, fg, boldp) + 1; /* white / light grey */
}
}
else if (red == 128)
{
if (green == 128 && blue == 128)
return lookup_color(12, fg, boldp) + 1; /* high intensity black / dark grey */
}
else if (red == 255)
{
if (green == 64)
{
if (blue == 64)
return lookup_color(20, fg, boldp) + 1; /* light red */
if (blue == 255)
return lookup_color(22, fg, boldp) + 1; /* light magenta */
}
else if (green == 255)
{
if (blue == 64)
return lookup_color(24, fg, boldp) + 1; /* yellow */
if (blue == 255)
return lookup_color(26, fg, boldp) + 1; /* white */
}
}
else if (red == 64)
{
if (green == 64)
{
if (blue == 255)
return lookup_color(14, fg, boldp) + 1; /* light blue */
}
else if (green == 255)
{
if (blue == 64)
return lookup_color(16, fg, boldp) + 1; /* light green */
if (blue == 255)
return lookup_color(18, fg, boldp) + 1; /* light cyan */
}
}
if (t_colors >= 256)
{
if (red == blue && red == green)
{
/* 24-color greyscale */
static int cutoff[23] = {
0x05, 0x10, 0x1B, 0x26, 0x31, 0x3C, 0x47, 0x52,
0x5D, 0x68, 0x73, 0x7F, 0x8A, 0x95, 0xA0, 0xAB,
0xB6, 0xC1, 0xCC, 0xD7, 0xE2, 0xED, 0xF9};
int i;
for (i = 0; i < 23; ++i)
if (red < cutoff[i])
return i + 233;
return 256;
}
/* 216-color cube */
return 17 + ((red + 25) / 0x33) * 36
+ ((green + 25) / 0x33) * 6
+ (blue + 25) / 0x33;
}
return 0;
}
/*
* Convert the attributes of a vterm cell into an attribute index.
*/
static int
cell2attr(VTermScreenCellAttrs cellattrs, VTermColor cellfg, VTermColor cellbg)
{
int attr = 0;
if (cellattrs.bold)
attr |= HL_BOLD;
if (cellattrs.underline)
attr |= HL_UNDERLINE;
if (cellattrs.italic)
attr |= HL_ITALIC;
if (cellattrs.strike)
attr |= HL_STANDOUT;
if (cellattrs.reverse)
attr |= HL_INVERSE;
#ifdef FEAT_GUI
if (gui.in_use)
{
guicolor_T fg, bg;
fg = gui_mch_get_rgb_color(cellfg.red, cellfg.green, cellfg.blue);
bg = gui_mch_get_rgb_color(cellbg.red, cellbg.green, cellbg.blue);
return get_gui_attr_idx(attr, fg, bg);
}
else
#endif
#ifdef FEAT_TERMGUICOLORS
if (p_tgc)
{
guicolor_T fg, bg;
fg = gui_get_rgb_color_cmn(cellfg.red, cellfg.green, cellfg.blue);
bg = gui_get_rgb_color_cmn(cellbg.red, cellbg.green, cellbg.blue);
return get_tgc_attr_idx(attr, fg, bg);
}
else
#endif
{
int bold = MAYBE;
int fg = color2index(&cellfg, TRUE, &bold);
int bg = color2index(&cellbg, FALSE, &bold);
/* with 8 colors set the bold attribute to get a bright foreground */
if (bold == TRUE)
attr |= HL_BOLD;
return get_cterm_attr_idx(attr, fg, bg);
}
return 0;
}
static int
handle_damage(VTermRect rect, void *user)
{
@@ -1498,18 +1668,32 @@ handle_damage(VTermRect rect, void *user)
handle_moverect(VTermRect dest, VTermRect src, void *user)
{
term_T *term = (term_T *)user;
win_T *wp;
/* Scrolling up is done much more efficiently by deleting lines instead of
* redrawing the text. */
if (dest.start_col == src.start_col
&& dest.end_col == src.end_col
&& dest.start_row < src.start_row)
{
win_T *wp;
VTermColor fg, bg;
VTermScreenCellAttrs attr;
int clear_attr;
/* Set the color to clear lines with. */
vterm_state_get_default_colors(vterm_obtain_state(term->tl_vterm),
&fg, &bg);
vim_memset(&attr, 0, sizeof(attr));
clear_attr = cell2attr(attr, fg, bg);
FOR_ALL_WINDOWS(wp)
{
if (wp->w_buffer == term->tl_buffer)
/* scrolling up is much more efficient when deleting lines */
win_del_lines(wp, dest.start_row,
src.start_row - dest.start_row, FALSE, FALSE);
src.start_row - dest.start_row, FALSE, FALSE,
clear_attr);
}
}
redraw_buf_later(term->tl_buffer, NOT_VALID);
return 1;
}
@@ -1773,169 +1957,6 @@ term_channel_closed(channel_T *ch)
}
}
/*
* Reverse engineer the RGB value into a cterm color index.
* First color is 1. Return 0 if no match found.
*/
static int
color2index(VTermColor *color, int fg, int *boldp)
{
int red = color->red;
int blue = color->blue;
int green = color->green;
/* The argument for lookup_color() is for the color_names[] table. */
if (red == 0)
{
if (green == 0)
{
if (blue == 0)
return lookup_color(0, fg, boldp) + 1; /* black */
if (blue == 224)
return lookup_color(1, fg, boldp) + 1; /* dark blue */
}
else if (green == 224)
{
if (blue == 0)
return lookup_color(2, fg, boldp) + 1; /* dark green */
if (blue == 224)
return lookup_color(3, fg, boldp) + 1; /* dark cyan */
}
}
else if (red == 224)
{
if (green == 0)
{
if (blue == 0)
return lookup_color(4, fg, boldp) + 1; /* dark red */
if (blue == 224)
return lookup_color(5, fg, boldp) + 1; /* dark magenta */
}
else if (green == 224)
{
if (blue == 0)
return lookup_color(6, fg, boldp) + 1; /* dark yellow / brown */
if (blue == 224)
return lookup_color(8, fg, boldp) + 1; /* white / light grey */
}
}
else if (red == 128)
{
if (green == 128 && blue == 128)
return lookup_color(12, fg, boldp) + 1; /* high intensity black / dark grey */
}
else if (red == 255)
{
if (green == 64)
{
if (blue == 64)
return lookup_color(20, fg, boldp) + 1; /* light red */
if (blue == 255)
return lookup_color(22, fg, boldp) + 1; /* light magenta */
}
else if (green == 255)
{
if (blue == 64)
return lookup_color(24, fg, boldp) + 1; /* yellow */
if (blue == 255)
return lookup_color(26, fg, boldp) + 1; /* white */
}
}
else if (red == 64)
{
if (green == 64)
{
if (blue == 255)
return lookup_color(14, fg, boldp) + 1; /* light blue */
}
else if (green == 255)
{
if (blue == 64)
return lookup_color(16, fg, boldp) + 1; /* light green */
if (blue == 255)
return lookup_color(18, fg, boldp) + 1; /* light cyan */
}
}
if (t_colors >= 256)
{
if (red == blue && red == green)
{
/* 24-color greyscale */
static int cutoff[23] = {
0x05, 0x10, 0x1B, 0x26, 0x31, 0x3C, 0x47, 0x52,
0x5D, 0x68, 0x73, 0x7F, 0x8A, 0x95, 0xA0, 0xAB,
0xB6, 0xC1, 0xCC, 0xD7, 0xE2, 0xED, 0xF9};
int i;
for (i = 0; i < 23; ++i)
if (red < cutoff[i])
return i + 233;
return 256;
}
/* 216-color cube */
return 17 + ((red + 25) / 0x33) * 36
+ ((green + 25) / 0x33) * 6
+ (blue + 25) / 0x33;
}
return 0;
}
/*
* Convert the attributes of a vterm cell into an attribute index.
*/
static int
cell2attr(VTermScreenCellAttrs cellattrs, VTermColor cellfg, VTermColor cellbg)
{
int attr = 0;
if (cellattrs.bold)
attr |= HL_BOLD;
if (cellattrs.underline)
attr |= HL_UNDERLINE;
if (cellattrs.italic)
attr |= HL_ITALIC;
if (cellattrs.strike)
attr |= HL_STANDOUT;
if (cellattrs.reverse)
attr |= HL_INVERSE;
#ifdef FEAT_GUI
if (gui.in_use)
{
guicolor_T fg, bg;
fg = gui_mch_get_rgb_color(cellfg.red, cellfg.green, cellfg.blue);
bg = gui_mch_get_rgb_color(cellbg.red, cellbg.green, cellbg.blue);
return get_gui_attr_idx(attr, fg, bg);
}
else
#endif
#ifdef FEAT_TERMGUICOLORS
if (p_tgc)
{
guicolor_T fg, bg;
fg = gui_get_rgb_color_cmn(cellfg.red, cellfg.green, cellfg.blue);
bg = gui_get_rgb_color_cmn(cellbg.red, cellbg.green, cellbg.blue);
return get_tgc_attr_idx(attr, fg, bg);
}
else
#endif
{
int bold = MAYBE;
int fg = color2index(&cellfg, TRUE, &bold);
int bg = color2index(&cellbg, FALSE, &bold);
/* with 8 colors set the bold attribute to get a bright foreground */
if (bold == TRUE)
attr |= HL_BOLD;
return get_cterm_attr_idx(attr, fg, bg);
}
return 0;
}
/*
* Called to update a window that contains an active terminal.
* Returns FAIL when there is no terminal running in this window or in
@@ -2709,12 +2730,14 @@ f_term_wait(typval_T *argvars, typval_T *rettv UNUSED)
}
}
# ifdef WIN3264
# if defined(WIN3264) || defined(PROTO)
/**************************************
* 2. MS-Windows implementation.
*/
# ifndef PROTO
#define WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN 1ul
#define WINPTY_SPAWN_FLAG_EXIT_AFTER_SHUTDOWN 2ull
@@ -2737,9 +2760,10 @@ HANDLE (*winpty_agent_process)(void*);
#define WINPTY_DLL "winpty.dll"
static HINSTANCE hWinPtyDLL = NULL;
# endif
int
dyn_winpty_init(void)
static int
dyn_winpty_init(int verbose)
{
int i;
static struct
@@ -2768,7 +2792,7 @@ dyn_winpty_init(void)
/* No need to initialize twice. */
if (hWinPtyDLL)
return 1;
return OK;
/* Load winpty.dll, prefer using the 'winptydll' option, fall back to just
* winpty.dll. */
if (*p_winptydll != NUL)
@@ -2777,8 +2801,10 @@ dyn_winpty_init(void)
hWinPtyDLL = vimLoadLib(WINPTY_DLL);
if (!hWinPtyDLL)
{
EMSG2(_(e_loadlib), *p_winptydll != NUL ? p_winptydll : WINPTY_DLL);
return 0;
if (verbose)
EMSG2(_(e_loadlib), *p_winptydll != NUL ? p_winptydll
: (char_u *)WINPTY_DLL);
return FAIL;
}
for (i = 0; winpty_entry[i].name != NULL
&& winpty_entry[i].ptr != NULL; ++i)
@@ -2786,12 +2812,13 @@ dyn_winpty_init(void)
if ((*winpty_entry[i].ptr = (FARPROC)GetProcAddress(hWinPtyDLL,
winpty_entry[i].name)) == NULL)
{
EMSG2(_(e_loadfunc), winpty_entry[i].name);
return 0;
if (verbose)
EMSG2(_(e_loadfunc), winpty_entry[i].name);
return FAIL;
}
}
return 1;
return OK;
}
/*
@@ -2813,7 +2840,7 @@ term_and_job_init(term_T *term, int rows, int cols, typval_T *argvar, jobopt_T *
garray_T ga;
char_u *cmd;
if (!dyn_winpty_init())
if (dyn_winpty_init(TRUE) == FAIL)
return FAIL;
if (argvar->v_type == VAR_STRING)
@@ -2977,6 +3004,13 @@ term_report_winsize(term_T *term, int rows, int cols)
winpty_set_size(term->tl_winpty, cols, rows, NULL);
}
int
terminal_enabled(void)
{
return dyn_winpty_init(FALSE) == OK;
}
# else
/**************************************

View File

@@ -55,4 +55,5 @@ source test_taglist.vim
source test_timers.vim
source test_true_false.vim
source test_unlet.vim
source test_virtualedit.vim
source test_window_cmd.vim

View File

@@ -1,20 +1,41 @@
" Tests for stat functions and checktime
func Test_existent_file()
func CheckFileTime(doSleep)
let fname = 'Xtest.tmp'
let result = 0
let ts = localtime()
if a:doSleep
sleep 1
endif
let fl = ['Hello World!']
call writefile(fl, fname)
let tf = getftime(fname)
if a:doSleep
sleep 1
endif
let te = localtime()
call assert_true(ts <= tf && tf <= te)
call assert_equal(strlen(fl[0] . "\n"), getfsize(fname))
call assert_equal('file', getftype(fname))
call assert_equal('rw-', getfperm(fname)[0:2])
let time_correct = (ts <= tf && tf <= te)
if a:doSleep || time_correct
call assert_true(time_correct)
call assert_equal(strlen(fl[0] . "\n"), getfsize(fname))
call assert_equal('file', getftype(fname))
call assert_equal('rw-', getfperm(fname)[0:2])
let result = 1
endif
call delete(fname)
return result
endfunc
func Test_existent_file()
" On some systems the file timestamp is rounded to a multiple of 2 seconds.
" We need to sleep to handle that, but that makes the test slow. First try
" without the sleep, and if it fails try again with the sleep.
if CheckFileTime(0) == 0
call CheckFileTime(1)
endif
endfunc
func Test_existent_directory()

View File

@@ -1,6 +1,6 @@
" Tests for the terminal window.
if !exists('*term_start')
if !has('terminal')
finish
endif
@@ -450,3 +450,23 @@ func Test_terminal_list_args()
exe buf . 'bwipe!'
call assert_equal("", bufname(buf))
endfunction
func Test_terminal_noblock()
let g:buf = term_start(&shell)
for c in ['a','b','c','d','e','f','g','h','i','j','k']
call term_sendkeys(g:buf, 'echo ' . repeat(c, 5000) . "\<cr>")
endfor
call term_sendkeys(g:buf, "echo done\<cr>")
let g:lnum = term_getsize(g:buf)[0] - 1
call WaitFor('term_getline(g:buf, g:lnum) =~ "done"', 3000)
call assert_match('done', term_getline(g:buf, g:lnum))
let g:job = term_getjob(g:buf)
call Stop_shell_in_terminal(g:buf)
call term_wait(g:buf)
unlet g:buf
unlet g:job
unlet g:lnum
bwipe
endfunc

View File

@@ -0,0 +1,31 @@
" Tests for 'virtualedit'.
func Test_yank_move_change()
split
call setline(1, [
\ "func foo() error {",
\ "\tif n, err := bar();",
\ "\terr != nil {",
\ "\t\treturn err",
\ "\t}",
\ "\tn = n * n",
\ ])
set virtualedit=all
set ts=4
function! MoveSelectionDown(count) abort
normal! m`
silent! exe "'<,'>move'>+".a:count
norm! ``
endfunction
xmap ]e :<C-U>call MoveSelectionDown(v:count1)<CR>
2
normal 2gg
normal J
normal jVj
normal ]e
normal ce
bwipe!
set virtualedit=
set ts=8
endfunc

View File

@@ -769,6 +769,46 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
971,
/**/
970,
/**/
969,
/**/
968,
/**/
967,
/**/
966,
/**/
965,
/**/
964,
/**/
963,
/**/
962,
/**/
961,
/**/
960,
/**/
959,
/**/
958,
/**/
957,
/**/
956,
/**/
955,
/**/
954,
/**/
953,
/**/
952,
/**/
951,
/**/