Merge PR #1820 'Reimplement builtin terminal UI with termkey/unibilium'

This commit is contained in:
Thiago de Arruda 2015-02-16 23:42:25 -03:00
commit 0429857689
51 changed files with 1503 additions and 4876 deletions

View File

@ -182,6 +182,14 @@ include_directories(SYSTEM ${MSGPACK_INCLUDE_DIRS})
find_package(LuaJit REQUIRED)
include_directories(SYSTEM ${LUAJIT_INCLUDE_DIRS})
set(LIBUNIBILIUM_USE_STATIC ON)
find_package(LibUnibilium REQUIRED)
include_directories(SYSTEM ${LIBUNIBILIUM_INCLUDE_DIRS})
set(LIBTERMKEY_USE_STATIC ON)
find_package(LibTermkey REQUIRED)
include_directories(SYSTEM ${LIBTERMEY_INCLUDE_DIRS})
find_package(LibIntl)
if(LibIntl_FOUND)
include_directories(SYSTEM ${LibIntl_INCLUDE_DIRS})

View File

@ -23,7 +23,6 @@ check_include_files(locale.h HAVE_LOCALE_H)
check_include_files(pwd.h HAVE_PWD_H)
check_include_files(strings.h HAVE_STRINGS_H)
check_include_files(stropts.h HAVE_STROPTS_H)
check_include_files(sys/ioctl.h HAVE_SYS_IOCTL_H)
check_include_files(sys/param.h HAVE_SYS_PARAM_H)
check_include_files(sys/time.h HAVE_SYS_TIME_H)
check_include_files(sys/wait.h HAVE_SYS_WAIT_H)
@ -33,9 +32,6 @@ if(NOT HAVE_SYS_WAIT_H AND UNIX)
endif()
check_include_files(sys/utsname.h HAVE_SYS_UTSNAME_H)
check_include_files(utime.h HAVE_UTIME_H)
check_include_files(termcap.h HAVE_TERMCAP_H)
check_include_files(termios.h HAVE_TERMIOS_H)
check_include_files(termio.h HAVE_TERMIO_H)
check_include_files(unistd.h HAVE_UNISTD_H)
check_include_files(utime.h HAVE_UTIME_H)

View File

@ -53,15 +53,10 @@
#cmakedefine HAVE_STRINGS_H
#cmakedefine HAVE_STRNCASECMP
#cmakedefine HAVE_STROPTS_H
#cmakedefine HAVE_SYS_IOCTL_H
#cmakedefine HAVE_SYS_PARAM_H
#cmakedefine HAVE_SYS_TIME_H
#cmakedefine HAVE_SYS_UTSNAME_H
#cmakedefine HAVE_SYS_WAIT_H
#cmakedefine HAVE_TERMCAP_H
#cmakedefine HAVE_TERMIOS_H
#cmakedefine HAVE_TERMIO_H
#define HAVE_TGETENT 1
#cmakedefine HAVE_UNISTD_H
#define HAVE_UP_BC_PC 1
#cmakedefine HAVE_UTIME
@ -70,8 +65,6 @@
#cmakedefine HAVE_WORKING_LIBINTL
#define RETSIGTYPE void
#define SIGRETURN return
#define TERMINFO 1
#define TGETENT_ZERO_ERR 0
#define TIME_WITH_SYS_TIME 1
#cmakedefine UNIX
#define USEMAN_S 1
@ -79,9 +72,3 @@
#define FEAT_BROWSE
#define FEAT_CSCOPE
#define FEAT_MOUSE
#define FEAT_MOUSE_NET
#define FEAT_MOUSE_SGR
#define FEAT_MOUSE_TTY
#define FEAT_MOUSE_URXVT
#define FEAT_MOUSE_XTERM
#define FEAT_TERMRESPONSE

View File

@ -287,7 +287,7 @@ MsgpackRpcRequestHandler msgpack_rpc_get_handler_for(const char *name,
{
String m = {
.data=(char *)name,
.size=min(name_len, ]]..max_fname_len..[[)
.size=MIN(name_len, ]]..max_fname_len..[[)
};
MsgpackRpcRequestHandler rv =
map_get(String, MsgpackRpcRequestHandler)(methods, m);

View File

@ -26,13 +26,16 @@ file(MAKE_DIRECTORY ${GENERATED_DIR}/os)
file(MAKE_DIRECTORY ${GENERATED_DIR}/api)
file(MAKE_DIRECTORY ${GENERATED_DIR}/api/private)
file(MAKE_DIRECTORY ${GENERATED_DIR}/msgpack_rpc)
file(MAKE_DIRECTORY ${GENERATED_DIR}/tui)
file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR})
file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/os)
file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/api)
file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/api/private)
file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/msgpack_rpc)
file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/tui)
file(GLOB NEOVIM_SOURCES *.c os/*.c api/*.c api/private/*.c msgpack_rpc/*.c)
file(GLOB NEOVIM_SOURCES *.c os/*.c api/*.c api/private/*.c msgpack_rpc/*.c
tui/*.c)
file(GLOB_RECURSE NEOVIM_HEADERS *.h)
foreach(sfile ${NEOVIM_SOURCES})
@ -164,19 +167,6 @@ if (LibIntl_FOUND)
list(APPEND NVIM_LINK_LIBRARIES ${LibIntl_LIBRARY})
endif()
check_library_exists(curses tgetent "" HAVE_LIBCURSES)
if (HAVE_LIBCURSES)
list(APPEND NVIM_LINK_LIBRARIES curses)
else()
check_library_exists(tinfo tgetent "" HAVE_LIBTINFO)
if (HAVE_LIBTINFO)
list(APPEND NVIM_LINK_LIBRARIES tinfo)
else()
find_package(Curses REQUIRED)
list(APPEND NVIM_LINK_LIBRARIES ${CURSES_LIBRARY})
endif()
endif()
if(Iconv_LIBRARIES)
list(APPEND NVIM_LINK_LIBRARIES ${Iconv_LIBRARIES})
endif()
@ -186,8 +176,12 @@ list(APPEND NVIM_LINK_LIBRARIES
${LIBUV_LIBRARIES}
${MSGPACK_LIBRARIES}
${LUAJIT_LIBRARIES}
${LIBTICKIT_LIBRARIES}
${LIBTERMKEY_LIBRARIES}
${LIBUNIBILIUM_LIBRARIES}
m
${CMAKE_THREAD_LIBS_INIT})
${CMAKE_THREAD_LIBS_INIT}
)
add_executable(nvim ${NEOVIM_GENERATED_SOURCES} ${NEOVIM_SOURCES}
${NEOVIM_HEADERS})

View File

@ -1849,25 +1849,6 @@ int hex2nr(int c)
return c - '0';
}
#if defined(FEAT_TERMRESPONSE) || defined(FEAT_GUI_GTK)
/// Convert two hex characters to a byte.
/// Return -1 if one of the characters is not hex.
///
/// @param p
///
/// @return The two hex characters converted to a byte or -1 if one of the
/// character is not hex.
int hexhex2nr(char_u *p)
{
if (!vim_isxdigit(p[0]) || !vim_isxdigit(p[1])) {
return -1;
}
return (hex2nr(p[0]) << 4) + hex2nr(p[1]);
}
#endif // if defined(FEAT_TERMRESPONSE) || defined(FEAT_GUI_GTK)
/// Return true if "str" starts with a backslash that should be removed.
/// For WIN32 this is only done when the character after the
/// backslash is not a normal file name character.

View File

@ -9955,14 +9955,8 @@ static void f_has(typval_T *argvars, typval_T *rettv)
#endif
"tag_binary",
"tag_old_static",
#ifdef TERMINFO
"terminfo",
#endif
"termresponse",
"textobjects",
#ifdef HAVE_TGETENT
"tgetent",
#endif
"title",
"user-commands", /* was accidentally included in 5.4 */
"user_commands",
@ -14424,13 +14418,10 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv)
if (argvars[2].v_type != VAR_UNKNOWN) {
mode = get_tv_string_buf(&argvars[2], modebuf);
modec = TOLOWER_ASC(mode[0]);
if (modec != 't' && modec != 'c' && modec != 'g')
if (modec != 'c' && modec != 'g')
modec = 0; /* replace invalid with current */
} else {
if (abstract_ui || t_colors > 1)
modec = 'c';
else
modec = 't';
modec = 'c';
}
@ -18080,21 +18071,6 @@ static int function_exists(char_u *name)
return n;
}
char_u *get_expanded_name(char_u *name, int check)
{
char_u *nm = name;
char_u *p;
p = trans_function_name(&nm, FALSE, TFN_INT|TFN_QUIET, NULL);
if (p != NULL && *nm == NUL)
if (!check || translated_function_exists(p))
return p;
free(p);
return NULL;
}
/// Return TRUE if "name" looks like a builtin function name: starts with a
/// lower case letter and doesn't contain AUTOLOAD_CHAR.
/// "len" is the length of "name", or -1 for NUL terminated.

View File

@ -1232,9 +1232,6 @@ do_shell (
* avoid having to type return below.
*/
msg_putchar('\r'); /* put cursor at start of line */
if (!autocmd_busy) {
stoptermcap();
}
msg_putchar('\n'); /* may shift screen one line up */
/* warning message before calling the shell */
@ -1292,8 +1289,6 @@ do_shell (
wait_return(msg_silent == 0);
no_wait_return = save_nwr;
}
starttermcap(); /* start termcap if not done by wait_return() */
}
/* display any error messages now */

View File

@ -2483,7 +2483,7 @@ return {
{
command='winpos',
flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
func='ex_winpos',
func='ex_ni',
},
{
command='wnext',

View File

@ -155,10 +155,6 @@ void do_debug(char_u *cmd)
#define CMD_INTERRUPT 6
/* Make sure we are in raw mode and start termcap mode. Might have side
* effects... */
starttermcap();
++RedrawingDisabled; /* don't redisplay the window */
++no_wait_return; /* don't wait for return */
did_emsg = FALSE; /* don't use error from debugged stuff */

View File

@ -146,10 +146,6 @@ struct dbg_stuff {
# define gui_mch_find_dialog ex_ni
# define gui_mch_replace_dialog ex_ni
# define ex_helpfind ex_ni
#if defined(FEAT_GUI) || defined(UNIX) || defined(MSWIN)
#else
# define ex_winpos ex_ni
#endif
static int did_lcd; /* whether ":lcd" was produced for a session */
#ifndef HAVE_WORKING_LIBINTL
# define ex_language ex_ni
@ -5397,13 +5393,11 @@ static void ex_stop(exarg_T *eap)
windgoto((int)Rows - 1, 0);
out_char('\n');
out_flush();
stoptermcap();
out_flush(); /* needed for SUN to restore xterm buffer */
mch_restore_title(3); /* restore window titles */
ui_suspend(); /* call machine specific function */
maketitle();
resettitle(); /* force updating the title */
starttermcap();
scroll_start(); /* scroll screen before redrawing */
redraw_later_clear();
shell_resized(); /* may have resized window */
@ -6438,7 +6432,7 @@ static void ex_winsize(exarg_T *eap)
p = arg;
h = getdigits_int(&arg);
if (*p != NUL && *arg == NUL)
screen_resize(w, h, TRUE);
screen_resize(w, h);
else
EMSG(_("E465: :winsize requires two number arguments"));
}
@ -6473,35 +6467,6 @@ static void ex_wincmd(exarg_T *eap)
}
}
#if defined(FEAT_GUI) || defined(UNIX) || defined(MSWIN)
/*
* ":winpos".
*/
static void ex_winpos(exarg_T *eap)
{
int x, y;
char_u *arg = eap->arg;
char_u *p;
if (*arg == NUL) {
EMSG(_("E188: Obtaining window position not implemented for this platform"));
} else {
x = getdigits_int(&arg);
arg = skipwhite(arg);
p = arg;
y = getdigits_int(&arg);
if (*p == NUL || *arg != NUL) {
EMSG(_("E466: :winpos requires two number arguments"));
return;
}
# ifdef HAVE_TGETENT
if (*T_CWP)
term_set_winpos(x, y);
# endif
}
}
#endif
/*
* Handle command that work like operators: ":delete", ":yank", ":>" and ":<".
*/

View File

@ -4618,35 +4618,6 @@ int del_history_idx(int histype, int idx)
return TRUE;
}
/*
* Very specific function to remove the value in ":set key=val" from the
* history.
*/
void remove_key_from_history(void)
{
char_u *p;
int i;
i = hisidx[HIST_CMD];
if (i < 0)
return;
p = history[HIST_CMD][i].hisstr;
if (p != NULL)
for (; *p; ++p)
if (STRNCMP(p, "key", 3) == 0 && !isalpha(p[3])) {
p = vim_strchr(p + 3, '=');
if (p == NULL)
break;
++p;
for (i = 0; p[i] && !vim_iswhite(p[i]); ++i)
if (p[i] == '\\' && p[i + 1])
++i;
STRMOVE(p, p + i);
--p;
}
}
/*
* Get indices "num1,num2" that specify a range within a list (not a range of
* text lines in a buffer!) from a string. Used for ":history" and ":clist".

View File

@ -1813,7 +1813,6 @@ failed:
* Switch on raw mode now and clear the screen.
*/
if (read_stdin) {
starttermcap();
screenclear();
}
@ -6471,12 +6470,6 @@ int has_cmdundefined(void)
return first_autopat[(int)EVENT_CMDUNDEFINED] != NULL;
}
/// @returns true when there is an FuncUndefined autocommand defined.
int has_funcundefined(void)
{
return first_autopat[(int)EVENT_FUNCUNDEFINED] != NULL;
}
static int
apply_autocmds_group (
event_T event,

View File

@ -198,23 +198,3 @@ void ga_append(garray_T *gap, char c)
{
GA_APPEND(char, gap, c);
}
#if defined(UNIX) || defined(WIN3264)
/// Append the text in "gap" below the cursor line and clear "gap".
///
/// @param gap
void append_ga_line(garray_T *gap)
{
// Remove trailing CR.
if (!GA_EMPTY(gap)
&& !curbuf->b_p_bin
&& (((char_u *)gap->ga_data)[gap->ga_len - 1] == CAR)) {
gap->ga_len--;
}
ga_append(gap, NUL);
ml_append(curwin->w_cursor.lnum++, gap->ga_data, 0, FALSE);
gap->ga_len = 0;
}
#endif // if defined(UNIX) || defined(WIN3264)

View File

@ -1203,7 +1203,6 @@ void save_typeahead(tasave_T *tp)
readbuf1.bh_first.b_next = NULL;
tp->save_readbuf2 = readbuf2;
readbuf2.bh_first.b_next = NULL;
tp->save_inputbuf = input_buffer_save();
}
/*
@ -1224,7 +1223,6 @@ void restore_typeahead(tasave_T *tp)
readbuf1 = tp->save_readbuf1;
free_buff(&readbuf2);
readbuf2 = tp->save_readbuf2;
input_buffer_restore(tp->save_inputbuf);
}
/*
@ -1545,22 +1543,6 @@ int vpeekc(void)
return vgetorpeek(FALSE);
}
/*
* Like vpeekc(), but don't allow mapping. Do allow checking for terminal
* codes.
*/
int vpeekc_nomap(void)
{
int c;
++no_mapping;
++allow_keys;
c = vpeekc();
--no_mapping;
--allow_keys;
return c;
}
/*
* Check if any character is available, also half an escape sequence.
* Trick: when no typeahead found, but there is something in the typeahead
@ -1929,8 +1911,6 @@ static int vgetorpeek(int advance)
if ((mp == NULL || max_mlen >= mp_match_len)
&& keylen != KEYLEN_PART_MAP) {
int save_keylen = keylen;
/*
* When no matching mapping found or found a
* non-matching mapping that matches at least what the
@ -1947,25 +1927,7 @@ static int vgetorpeek(int advance)
|| (p_remap && typebuf.tb_noremap[
typebuf.tb_off] == RM_YES))
&& !timedout) {
keylen = check_termcode(max_mlen + 1,
NULL, 0, NULL);
/* If no termcode matched but 'pastetoggle'
* matched partially it's like an incomplete key
* sequence. */
if (keylen == 0 && save_keylen == KEYLEN_PART_KEY)
keylen = KEYLEN_PART_KEY;
/*
* When getting a partial match, but the last
* characters were not typed, don't wait for a
* typed character to complete the termcode.
* This helps a lot when a ":normal" command ends
* in an ESC.
*/
if (keylen < 0
&& typebuf.tb_len == typebuf.tb_maplen)
keylen = 0;
keylen = 0;
} else
keylen = 0;
if (keylen == 0) { /* no matching terminal code */
@ -2515,29 +2477,27 @@ fix_input_buffer (
int script /* TRUE when reading from a script */
)
{
if (abstract_ui) {
// Should not escape K_SPECIAL/CSI while in embedded mode because vim key
// codes keys are processed in input.c/input_enqueue.
if (!using_script()) {
// Should not escape K_SPECIAL/CSI reading input from the user because vim
// key codes keys are processed in input.c/input_enqueue.
buf[len] = NUL;
return len;
}
// Reading from script, need to process special bytes
int i;
char_u *p = buf;
/*
* Two characters are special: NUL and K_SPECIAL.
* When compiled With the GUI CSI is also special.
* Replace NUL by K_SPECIAL KS_ZERO KE_FILLER
* Replace K_SPECIAL by K_SPECIAL KS_SPECIAL KE_FILLER
* Replace CSI by K_SPECIAL KS_EXTRA KE_CSI
* Don't replace K_SPECIAL when reading a script file.
*/
// Two characters are special: NUL and K_SPECIAL.
// Replace NUL by K_SPECIAL KS_ZERO KE_FILLER
// Replace K_SPECIAL by K_SPECIAL KS_SPECIAL KE_FILLER
// Replace CSI by K_SPECIAL KS_EXTRA KE_CSI
// Don't replace K_SPECIAL when reading a script file.
for (i = len; --i >= 0; ++p) {
if (p[0] == NUL
|| (p[0] == K_SPECIAL
&& !script
&& (i < 2 || p[1] != KS_EXTRA || is_user_input(p[2])))) {
&& (i < 2 || p[1] != KS_EXTRA))) {
memmove(p + 3, p + 1, (size_t)i);
p[2] = K_THIRD(p[0]);
p[1] = K_SECOND(p[0]);
@ -2546,7 +2506,7 @@ fix_input_buffer (
len += 2;
}
}
*p = NUL; /* add trailing NUL */
*p = NUL; // add trailing NUL
return len;
}
@ -3772,11 +3732,6 @@ eval_map_expr (
return res;
}
static bool is_user_input(int k)
{
return k != (int)KE_EVENT && k != (int)KE_CURSORHOLD;
}
/*
* Copy "p" to allocated memory, escaping K_SPECIAL and CSI so that the result
* can be put in the typeahead buffer.

View File

@ -904,13 +904,10 @@ EXTERN char_u *use_viminfo INIT(= NULL); /* name of viminfo file to use */
EXTERN FILE *scriptin[NSCRIPT]; /* streams to read script from */
EXTERN int curscript INIT(= 0); /* index in scriptin[] */
EXTERN FILE *scriptout INIT(= NULL); /* stream to write script to */
EXTERN int read_cmd_fd INIT(= 0); /* fd to read commands from */
/* volatile because it is used in signal handler catch_sigint(). */
EXTERN volatile int got_int INIT(= FALSE); /* set to TRUE when interrupt
signal occurred */
EXTERN int termcap_active INIT(= FALSE); /* set by starttermcap() */
EXTERN int cur_tmode INIT(= TMODE_COOK); /* input terminal mode */
EXTERN int bangredo INIT(= FALSE); /* set to TRUE with ! command */
EXTERN int searchcmdlen; /* length of previous search cmd */
EXTERN int reg_do_extmatch INIT(= 0); /* Used when compiling regexp:
@ -1237,14 +1234,8 @@ EXTERN FILE *time_fd INIT(= NULL); /* where to write startup timing */
EXTERN int ignored;
EXTERN char *ignoredp;
/* Temporarily moved these static variables to assist in migrating from
* os_unix.c */
EXTERN int curr_tmode INIT(= TMODE_COOK); /* contains current terminal mode */
// If a msgpack-rpc channel should be started over stdin/stdout
EXTERN bool embedded_mode INIT(= false);
// Using the "abstract_ui" termcap
EXTERN bool abstract_ui INIT(= false);
/// Used to track the status of external functions.
/// Currently only used for iconv().

View File

@ -617,10 +617,7 @@ void ex_hardcopy(exarg_T *eap)
eap->forceit) == FAIL)
return;
if (t_colors > 1)
settings.modec = 'c';
else
settings.modec = 't';
settings.modec = 'c';
if (!syntax_present(curwin))
settings.do_syntax = FALSE;

View File

@ -752,26 +752,3 @@ int get_mouse_button(int code, bool *is_click, bool *is_drag)
}
return 0; /* Shouldn't get here */
}
/*
* Return the appropriate pseudo mouse event token (KE_LEFTMOUSE etc) based on
* the given information about which mouse button is down, and whether the
* mouse was clicked, dragged or released.
*/
int
get_pseudo_mouse_code (
int button, /* eg MOUSE_LEFT */
int is_click,
int is_drag
)
{
int i;
for (i = 0; mouse_table[i].pseudo_code; i++)
if (button == mouse_table[i].button
&& is_click == mouse_table[i].is_click
&& is_drag == mouse_table[i].is_drag) {
return mouse_table[i].pseudo_code;
}
return (int)KE_IGNORE; /* not recognized, ignore it */
}

View File

@ -95,7 +95,12 @@ typedef struct {
char_u *use_ef; /* 'errorfile' from -q argument */
int want_full_screen;
bool stdout_isatty; /* is stdout a terminal? */
bool input_isatty; // stdin is a terminal
bool output_isatty; // stdout is a terminal
bool err_isatty; // stderr is a terminal
bool headless; // Dont try to start an user interface
// or read/write to stdio(unless
// embedding)
char_u *term; /* specified terminal name */
int no_swap_file; /* "-n" argument used */
int use_debug_break_level;
@ -197,9 +202,7 @@ int main(int argc, char **argv)
early_init();
/*
* Check if we have an interactive window.
*/
// Check if we have an interactive window.
check_and_set_isatty(&params);
/*
@ -242,34 +245,20 @@ int main(int argc, char **argv)
printf(_("%d files to edit\n"), GARGCOUNT);
if (params.want_full_screen && !silent_mode) {
if (embedded_mode) {
// embedded mode implies abstract_ui
termcapinit((uint8_t *)"abstract_ui");
} else {
// set terminal name and get terminal capabilities (will set full_screen)
// Do some initialization of the screen
termcapinit(params.term);
}
termcapinit((uint8_t *)"abstract_ui");
screen_start(); /* don't know where cursor is now */
TIME_MSG("Termcap init");
}
event_init();
if (abstract_ui) {
full_screen = true;
t_colors = 256;
T_CCO = (uint8_t *)"256";
} else {
// Print a warning if stdout is not a terminal TODO(tarruda): Remove this
// check once the new terminal UI is implemented
check_tty(&params);
}
full_screen = true;
t_colors = 256;
T_CCO = (uint8_t *)"256";
check_tty(&params);
/*
* Set the default values for the options that use Rows and Columns.
*/
ui_get_shellsize(); /* inits Rows and Columns */
win_init_size();
/* Set the 'diff' option now, so that it can be checked for in a .vimrc
* file. There is no buffer yet though. */
@ -382,25 +371,22 @@ int main(int argc, char **argv)
newline_on_exit = TRUE;
#endif
/*
* When done something that is not allowed or error message call
* wait_return. This must be done before starttermcap(), because it may
* switch to another screen. It must be done after settmode(TMODE_RAW),
* because we want to react on a single key stroke.
* Call settmode and starttermcap here, so the T_KS and T_TI may be
* defined by termcapinit and redefined in .exrc.
*/
settmode(TMODE_RAW);
TIME_MSG("setting raw mode");
if (!params.headless && (params.output_isatty || params.err_isatty)) {
if (params.input_isatty && (need_wait_return || msg_didany)) {
// Since at this point there's no UI instance running yet, error messages
// would have been printed to stdout. Before starting (which can result
// in a alternate screen buffer being shown) we need confirmation that
// the user has seen the messages and that is done with a call to
// wait_return. For that to work, stdin must be openend temporarily.
input_start_stdin();
wait_return(TRUE);
TIME_MSG("waiting for return");
input_stop_stdin();
}
if (need_wait_return || msg_didany) {
wait_return(TRUE);
TIME_MSG("waiting for return");
ui_builtin_start();
}
starttermcap(); // start termcap if not done by wait_return()
TIME_MSG("start termcap");
may_req_ambiguous_char_width();
setmouse(); // may start using the mouse
if (scroll_region) {
@ -480,10 +466,6 @@ int main(int argc, char **argv)
no_wait_return = FALSE;
starting = 0;
/* Requesting the termresponse is postponed until here, so that a "-c q"
* argument doesn't make it appear in the shell Vim was started from. */
may_req_termresponse();
/* start in insert mode */
if (p_im)
need_start_insertmode = TRUE;
@ -965,14 +947,14 @@ static void command_line_scan(mparm_T *parmp)
c = argv[0][argv_idx++];
switch (c) {
case NUL: /* "vim -" read from stdin */
/* "ex -" silent mode */
if (exmode_active)
if (exmode_active) {
// "ex -" silent mode
silent_mode = TRUE;
else {
if (parmp->edit_type != EDIT_NONE)
} else {
if (parmp->edit_type != EDIT_NONE) {
mainerr(ME_TOO_MANY_ARGS, argv[0]);
}
parmp->edit_type = EDIT_STDIN;
read_cmd_fd = 2; /* read from stderr instead of stdin */
}
argv_idx = -1; /* skip to next argument */
break;
@ -1004,8 +986,11 @@ static void command_line_scan(mparm_T *parmp)
}
mch_exit(0);
} else if (STRICMP(argv[0] + argv_idx, "headless") == 0) {
parmp->headless = true;
} else if (STRICMP(argv[0] + argv_idx, "embed") == 0) {
embedded_mode = true;
parmp->headless = true;
} else if (STRNICMP(argv[0] + argv_idx, "literal", 7) == 0) {
#if !defined(UNIX)
parmp->literal = TRUE;
@ -1418,7 +1403,8 @@ static void init_params(mparm_T *paramp, int argc, char **argv)
memset(paramp, 0, sizeof(*paramp));
paramp->argc = argc;
paramp->argv = argv;
paramp->want_full_screen = TRUE;
paramp->headless = false;
paramp->want_full_screen = true;
paramp->use_debug_break_level = -1;
paramp->window_count = -1;
}
@ -1439,15 +1425,13 @@ static void init_startuptime(mparm_T *paramp)
starttime = time(NULL);
}
/*
* Check if we have an interactive window.
*/
static void check_and_set_isatty(mparm_T *paramp)
{
paramp->stdout_isatty = os_isatty(STDOUT_FILENO);
paramp->input_isatty = os_isatty(fileno(stdin));
paramp->output_isatty = os_isatty(fileno(stdout));
paramp->err_isatty = os_isatty(fileno(stderr));
TIME_MSG("window checked");
}
/*
* Get filename from command line, given that there is one.
*/
@ -1532,28 +1516,43 @@ static void handle_tag(char_u *tagname)
}
}
/*
* Print a warning if stdout is not a terminal.
* When starting in Ex mode and commands come from a file, set Silent mode.
*/
// Print a warning if stdout is not a terminal.
// When starting in Ex mode and commands come from a file, set Silent mode.
static void check_tty(mparm_T *parmp)
{
if (parmp->headless) {
return;
}
// is active input a terminal?
bool input_isatty = os_isatty(read_cmd_fd);
if (exmode_active) {
if (!input_isatty)
silent_mode = TRUE;
} else if (parmp->want_full_screen && (!parmp->stdout_isatty || !input_isatty)
) {
if (!parmp->stdout_isatty)
if (!parmp->input_isatty) {
silent_mode = true;
}
} else if (parmp->want_full_screen && (!parmp->err_isatty
&& (!parmp->output_isatty || !parmp->input_isatty))) {
if (!parmp->output_isatty) {
mch_errmsg(_("Vim: Warning: Output is not to a terminal\n"));
if (!input_isatty)
}
if (!parmp->input_isatty) {
mch_errmsg(_("Vim: Warning: Input is not from a terminal\n"));
}
out_flush();
if (scriptin[0] == NULL)
if (scriptin[0] == NULL) {
os_delay(2000L, true);
}
TIME_MSG("Warning delay");
}
if (parmp->edit_type != EDIT_STDIN && !parmp->input_isatty) {
// read commands from directly from stdin
input_start_stdin();
}
}
/*
@ -1573,13 +1572,6 @@ static void read_stdin(void)
msg_didany = i;
TIME_MSG("reading stdin");
check_swap_exists_action();
/*
* Close stdin and dup it from stderr. Required for GPM to work
* properly, and for running external commands.
* Is there any other system that cannot do this?
*/
close(0);
ignored = dup(2);
}
/*
@ -2104,6 +2096,7 @@ static void usage(void)
mch_msg(_(" -i <nviminfo> Use <nviminfo> instead of .nviminfo\n"));
mch_msg(_(" --api-info Dump API metadata serialized to msgpack and exit\n"));
mch_msg(_(" --embed Use stdin/stdout as a msgpack-rpc channel\n"));
mch_msg(_(" --headless Don't start a user interface\n"));
mch_msg(_(" --version Print version information and exit\n"));
mch_msg(_(" -h | --help Print this help message and exit\n"));

View File

@ -100,7 +100,7 @@ static inline khint_t String_hash(String s)
static inline bool String_eq(String a, String b)
{
return strncmp(a.data, b.data, min(a.size, b.size)) == 0;
return strncmp(a.data, b.data, MIN(a.size, b.size)) == 0;
}

View File

@ -40,6 +40,7 @@
#include "nvim/screen.h"
#include "nvim/strings.h"
#include "nvim/term.h"
#include "nvim/ui.h"
#include "nvim/mouse.h"
#include "nvim/os/os.h"
#include "nvim/os/input.h"
@ -915,15 +916,6 @@ void wait_return(int redraw)
State = oldState; /* restore State before set_shellsize */
setmouse();
msg_check();
#if defined(UNIX)
/*
* When switching screens, we need to output an extra newline on exit.
*/
if (swapping_screen() && !termcap_active)
newline_on_exit = TRUE;
#endif
need_wait_return = FALSE;
did_wait_return = TRUE;
emsg_on_display = FALSE; /* can delete error message now */
@ -936,11 +928,9 @@ void wait_return(int redraw)
}
if (tmpState == SETWSIZE) { /* got resize event while in vgetc() */
starttermcap(); /* start termcap before redrawing */
shell_resized();
} else if (!skip_redraw
&& (redraw == TRUE || (msg_scrolled != 0 && redraw != -1))) {
starttermcap(); /* start termcap before redrawing */
redraw_later(VALID);
}
}
@ -978,17 +968,6 @@ void set_keep_msg(char_u *s, int attr)
keep_msg_attr = attr;
}
/*
* If there currently is a message being displayed, set "keep_msg" to it, so
* that it will be displayed again after redraw.
*/
void set_keep_msg_from_hist(void)
{
if (keep_msg == NULL && last_msg_hist != NULL && msg_scrolled == 0
&& (State & NORMAL))
set_keep_msg(last_msg_hist->msg, last_msg_hist->attr);
}
/*
* Prepare for outputting characters in the command line.
*/
@ -1780,19 +1759,6 @@ static void msg_scroll_up(void)
{
/* scrolling up always works */
screen_del_lines(0, 0, 1, (int)Rows, TRUE, NULL);
if (!can_clear((char_u *)" ")) {
/* Scrolling up doesn't result in the right background. Set the
* background here. It's not efficient, but avoids that we have to do
* it all over the code. */
screen_fill((int)Rows - 1, (int)Rows, 0, (int)Columns, ' ', ' ', 0);
/* Also clear the last char of the last but one line if it was not
* cleared before to avoid a scroll-up. */
if (ScreenAttrs[LineOffset[Rows - 2] + Columns - 1] == (sattr_T)-1)
screen_fill((int)Rows - 2, (int)Rows - 1,
(int)Columns - 1, (int)Columns, ' ', ' ', 0);
}
}
/*
@ -1975,19 +1941,11 @@ static void t_puts(int *t_col, char_u *t_s, char_u *s, int attr)
}
}
/*
* Returns TRUE when messages should be printed with mch_errmsg().
* This is used when there is no valid screen, so we can see error messages.
* If termcap is not active, we may be writing in an alternate console
* window, cursor positioning may not work correctly (window size may be
* different, e.g. for Win32 console) or we just don't know where the
* cursor is.
*/
// Returns TRUE when messages should be printed to stdout/stderr, which
// happens when no UIs are attached and nvim is not being embedded
int msg_use_printf(void)
{
return !msg_check_screen()
|| (swapping_screen() && !termcap_active)
;
return !embedded_mode && !ui_active();
}
/*
@ -2378,24 +2336,6 @@ void repeat_message(void)
}
}
/*
* msg_check_screen - check if the screen is initialized.
* Also check msg_row and msg_col, if they are too big it may cause a crash.
* While starting the GUI the terminal codes will be set for the GUI, but the
* output goes to the terminal. Don't use the terminal codes then.
*/
static int msg_check_screen(void)
{
if (!full_screen || !screen_valid(FALSE))
return FALSE;
if (msg_row >= Rows)
msg_row = Rows - 1;
if (msg_col >= Columns)
msg_col = Columns - 1;
return TRUE;
}
/*
* Clear from current message position to end of screen.
* Skip this when ":silent" was used, no need to clear for redirection.

View File

@ -2395,11 +2395,6 @@ int get_keystroke(void)
} else if (len > 0)
++waited; /* keep track of the waiting time */
/* Incomplete termcode and not timed out yet: get more characters */
if ((n = check_termcode(1, buf, buflen, &len)) < 0
&& (!p_ttimeout || waited * 100L < (p_ttm < 0 ? p_tm : p_ttm)))
continue;
if (n == KEYLEN_REMOVED) { /* key code removed */
if (must_redraw != 0 && !need_wait_return && (State & CMDLINE) == 0) {
/* Redrawing was postponed, do it now. */
@ -3301,28 +3296,6 @@ home_replace_save (
return dst;
}
void prepare_to_exit(void)
{
#if defined(SIGHUP) && defined(SIG_IGN)
/* Ignore SIGHUP, because a dropped connection causes a read error, which
* makes Vim exit and then handling SIGHUP causes various reentrance
* problems. */
signal(SIGHUP, SIG_IGN);
#endif
{
windgoto((int)Rows - 1, 0);
/*
* Switch terminal mode back now, so messages end up on the "normal"
* screen (if there are two screens).
*/
settmode(TMODE_COOK);
stoptermcap();
out_flush();
}
}
/*
* Preserve files and exit.
* When called IObuff must contain a message.
@ -3340,9 +3313,6 @@ void preserve_exit(void)
}
really_exiting = true;
prepare_to_exit();
out_str(IObuff);
screen_start(); // don't know where cursor is now
out_flush();

View File

@ -451,12 +451,6 @@ void setmouse(void)
if (*p_mouse == NUL)
return;
/* don't switch mouse on when not in raw mode (Ex mode) */
if (!abstract_ui && cur_tmode != TMODE_RAW) {
mch_setmouse(false);
return;
}
if (VIsual_active)
checkfor = MOUSE_VISUAL;
else if (State == HITRETURN || State == ASKMORE || State == SETWSIZE)

View File

@ -103,9 +103,7 @@ void channel_init(void)
channel_from_stdio();
}
if (abstract_ui) {
remote_ui_init();
}
remote_ui_init();
}
/// Teardown the module
@ -176,13 +174,6 @@ void channel_from_stream(uv_stream_t *stream)
channel->data.streams.uv = stream;
}
bool channel_exists(uint64_t id)
{
Channel *channel;
return (channel = pmap_get(uint64_t)(channels, id)) != NULL
&& !channel->closed;
}
/// Sends event/arguments to channel
///
/// @param id The channel id. If 0, the event will be sent to all
@ -665,10 +656,7 @@ static void on_stdio_close(Event e)
static void free_channel(Channel *channel)
{
if (abstract_ui) {
remote_ui_disconnect(channel->id);
}
remote_ui_disconnect(channel->id);
pmap_del(uint64_t)(channels, channel->id);
msgpack_unpacker_free(channel->unpacker);

View File

@ -295,22 +295,6 @@ void msgpack_rpc_from_dictionary(Dictionary result, msgpack_packer *res)
}
}
/// Finishes the msgpack-rpc call with an error message.
///
/// @param msg The error message
/// @param res A packer that contains the response
void msgpack_rpc_error(char *msg, msgpack_packer *res)
FUNC_ATTR_NONNULL_ALL
{
size_t len = strlen(msg);
// error message
msgpack_pack_bin(res, len);
msgpack_pack_bin_body(res, msg, len);
// Nil result
msgpack_pack_nil(res);
}
/// Handler executed when an invalid method name is passed
Object msgpack_rpc_handle_missing_method(uint64_t channel_id,
uint64_t request_id,

View File

@ -1834,7 +1834,6 @@ do_mouse (
bool fixindent /* PUT_FIXINDENT if fixing indent necessary */
)
{
static bool do_always = false; /* ignore 'mouse' setting next time */
static bool got_click = false; /* got a click some time back */
int which_button; /* MOUSE_LEFT, _MIDDLE or _RIGHT */
@ -1859,23 +1858,6 @@ do_mouse (
save_cursor = curwin->w_cursor;
// When "abstract_ui" is active, always recognize mouse events, otherwise:
// - Ignore mouse event in normal mode if 'mouse' doesn't include 'n'.
// - Ignore mouse event in visual mode if 'mouse' doesn't include 'v'.
// - For command line and insert mode 'mouse' is checked before calling
// do_mouse().
if (!abstract_ui) {
if (do_always)
do_always = false;
else {
if (VIsual_active) {
if (!mouse_has(MOUSE_VISUAL))
return false;
} else if (State == NORMAL && !mouse_has(MOUSE_NORMAL))
return false;
}
}
for (;; ) {
which_button = get_mouse_button(KEY2TERMCAP1(c), &is_click, &is_drag);
if (is_drag) {
@ -1996,7 +1978,6 @@ do_mouse (
stuffcharReadbuff('y');
stuffcharReadbuff(K_MIDDLEMOUSE);
}
do_always = true; /* ignore 'mouse' setting next time */
return false;
}
/*

View File

@ -4226,23 +4226,6 @@ did_set_string_option (
}
#if defined(FEAT_MOUSE_TTY) && defined(UNIX)
/* 'ttymouse' */
else if (varp == &p_ttym) {
/* Switch the mouse off before changing the escape sequences used for
* that. */
mch_setmouse(FALSE);
if (opt_strings_flags(p_ttym, p_ttym_values, &ttym_flags, FALSE) != OK)
errmsg = e_invarg;
else
check_mouse_termcode();
if (termcap_active)
setmouse(); /* may switch it on again */
}
#endif
/* 'selection' */
else if (varp == &p_sel) {
if (*p_sel == NUL
@ -5439,8 +5422,7 @@ set_num_option (
curbuf->b_p_iminsert = B_IMODE_NONE;
}
p_iminsert = curbuf->b_p_iminsert;
if (termcap_active) /* don't do this in the alternate screen */
showmode();
showmode();
/* Show/unshow value of 'keymap' in status lines. */
status_redraw_curbuf();
} else if (pp == &p_window) {
@ -5559,12 +5541,11 @@ set_num_option (
*/
if (old_Rows != Rows || old_Columns != Columns) {
/* Changing the screen size is not allowed while updating the screen. */
if (updating_screen)
if (updating_screen) {
*pp = old_value;
else if (full_screen
)
screen_resize((int)Columns, (int)Rows, TRUE);
else {
} else if (full_screen) {
screen_resize((int)Columns, (int)Rows);
} else {
/* Postpone the resizing; check the size and cmdline position for
* messages. */
check_shellsize();
@ -5970,27 +5951,6 @@ set_option_value (
return NULL;
}
/*
* Get the terminal code for a terminal option.
* Returns NULL when not found.
*/
char_u *get_term_code(char_u *tname)
{
int opt_idx;
char_u *varp;
if (tname[0] != 't' || tname[1] != '_' ||
tname[2] == NUL || tname[3] == NUL)
return NULL;
if ((opt_idx = findoption(tname)) >= 0) {
varp = get_varp(&(options[opt_idx]));
if (varp != NULL)
varp = *(char_u **)(varp);
return varp;
}
return find_termcode(tname + 2);
}
char_u *get_highlight_default(void)
{
int i;
@ -6414,7 +6374,6 @@ void clear_termoptions(void)
*/
mch_setmouse(FALSE); /* switch mouse off */
mch_restore_title(3); /* restore window titles */
stoptermcap(); /* stop termcap mode */
free_termoptions();
}
@ -7831,17 +7790,6 @@ int option_was_set(char_u *name)
return FALSE;
}
/*
* Reset the flag indicating option "name" was set.
*/
void reset_option_was_set(char_u *name)
{
int idx = findoption(name);
if (idx >= 0)
options[idx].flags &= ~P_WAS_SET;
}
/*
* fill_breakat_flags() -- called when 'breakat' changes value.
*/

View File

@ -54,7 +54,6 @@ void event_init(void)
wstream_init();
// Initialize input events
input_init();
input_start();
// Timer to wake the event loop if a timeout argument is passed to
// `event_poll`
// Signals
@ -75,13 +74,11 @@ void event_teardown(void)
process_events_from(immediate_events);
process_events_from(deferred_events);
input_stop_stdin();
channel_teardown();
job_teardown();
server_teardown();
signal_teardown();
input_stop();
input_teardown();
// this last `uv_run` will return after all handles are stopped, it will
// also take care of finishing any uv_close calls made by other *_teardown
// functions.

View File

@ -19,7 +19,6 @@
#include "nvim/fileio.h"
#include "nvim/ex_cmds2.h"
#include "nvim/getchar.h"
#include "nvim/term.h"
#include "nvim/main.h"
#include "nvim/misc1.h"
@ -32,9 +31,9 @@ typedef enum {
kInputEof
} InbufPollResult;
static RStream *read_stream;
static RBuffer *read_buffer, *input_buffer;
static bool eof = false, started_reading = false;
static RStream *read_stream = NULL;
static RBuffer *read_buffer = NULL, *input_buffer = NULL;
static bool eof = false;
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "os/input.c.generated.h"
@ -45,43 +44,29 @@ static bool eof = false, started_reading = false;
void input_init(void)
{
input_buffer = rbuffer_new(INPUT_BUFFER_SIZE + MAX_KEY_CODE_LEN);
}
if (abstract_ui) {
void input_start_stdin(void)
{
if (read_stream) {
return;
}
read_buffer = rbuffer_new(READ_BUFFER_SIZE);
read_stream = rstream_new(read_cb, read_buffer, NULL);
rstream_set_file(read_stream, read_cmd_fd);
}
void input_teardown(void)
{
if (abstract_ui) {
return;
}
rstream_free(read_stream);
}
// Listen for input
void input_start(void)
{
if (abstract_ui) {
return;
}
rstream_set_file(read_stream, fileno(stdin));
rstream_start(read_stream);
}
// Stop listening for input
void input_stop(void)
void input_stop_stdin(void)
{
if (abstract_ui) {
if (!read_stream) {
return;
}
rstream_stop(read_stream);
rstream_free(read_stream);
read_stream = NULL;
}
// Low level input function.
@ -141,11 +126,9 @@ bool os_char_avail(void)
}
// Check for CTRL-C typed by reading all available characters.
// In cooked mode we should get SIGINT, no need to check.
void os_breakcheck(void)
{
if (curr_tmode == TMODE_RAW)
input_poll(0);
event_poll(0);
}
/// Test whether a file descriptor refers to a terminal.
@ -157,34 +140,13 @@ bool os_isatty(int fd)
return uv_guess_handle(fd) == UV_TTY;
}
/// Return the contents of the input buffer and make it empty. The returned
/// pointer must be passed to `input_buffer_restore()` later.
String input_buffer_save(void)
{
size_t inbuf_size = rbuffer_pending(input_buffer);
String rv = {
.data = xmemdup(rbuffer_read_ptr(input_buffer), inbuf_size),
.size = inbuf_size
};
rbuffer_consumed(input_buffer, inbuf_size);
return rv;
}
/// Restore the contents of the input buffer and free `str`
void input_buffer_restore(String str)
{
rbuffer_consumed(input_buffer, rbuffer_pending(input_buffer));
rbuffer_write(input_buffer, str.data, str.size);
free(str.data);
}
size_t input_enqueue(String keys)
{
char *ptr = keys.data, *end = ptr + keys.size;
while (rbuffer_available(input_buffer) >= 6 && ptr < end) {
uint8_t buf[6] = {0};
unsigned int new_size = trans_special((uint8_t **)&ptr, buf, false);
unsigned int new_size = trans_special((uint8_t **)&ptr, buf, true);
if (!new_size) {
if (*ptr == '<') {
@ -215,15 +177,19 @@ static unsigned int handle_mouse_event(char **ptr, uint8_t *buf,
unsigned int bufsize)
{
int mouse_code = 0;
int type = 0;
if (bufsize == 3) {
mouse_code = buf[2];
type = buf[1];
} else if (bufsize == 6) {
// prefixed with K_SPECIAL KS_MODIFIER mod
mouse_code = buf[5];
type = buf[4];
}
if (!((mouse_code >= KE_LEFTMOUSE && mouse_code <= KE_RIGHTRELEASE)
if (type != KS_EXTRA
|| !((mouse_code >= KE_LEFTMOUSE && mouse_code <= KE_RIGHTRELEASE)
|| (mouse_code >= KE_MOUSEDOWN && mouse_code <= KE_MOUSERIGHT))) {
return bufsize;
}
@ -298,7 +264,7 @@ static bool input_poll(int ms)
prof_inchar_enter();
}
event_poll_until(ms, input_ready());
event_poll_until(ms, input_ready() || eof);
if (do_profiling == PROF_YES && ms) {
prof_inchar_exit();
@ -307,96 +273,31 @@ static bool input_poll(int ms)
return input_ready();
}
void input_done(void)
{
eof = true;
}
// This is a replacement for the old `WaitForChar` function in os_unix.c
static InbufPollResult inbuf_poll(int ms)
{
if (typebuf_was_filled || rbuffer_pending(input_buffer)) {
if (input_ready() || input_poll(ms)) {
return kInputAvail;
}
if (input_poll(ms)) {
return eof && rstream_pending(read_stream) == 0 ?
kInputEof :
kInputAvail;
}
return kInputNone;
}
static void stderr_switch(void)
{
int mode = cur_tmode;
// We probably set the wrong file descriptor to raw mode. Switch back to
// cooked mode
settmode(TMODE_COOK);
// Stop the idle handle
rstream_stop(read_stream);
// Use stderr for stdin, also works for shell commands.
read_cmd_fd = 2;
// Initialize and start the input stream
rstream_set_file(read_stream, read_cmd_fd);
rstream_start(read_stream);
// Set the mode back to what it was
settmode(mode);
return eof ? kInputEof : kInputNone;
}
static void read_cb(RStream *rstream, void *data, bool at_eof)
{
if (at_eof) {
if (!started_reading
&& rstream_is_regular_file(rstream)
&& os_isatty(STDERR_FILENO)) {
// Read error. Since stderr is a tty we switch to reading from it. This
// is for handling for cases like "foo | xargs vim" because xargs
// redirects stdin from /dev/null. Previously, this was done in ui.c
stderr_switch();
} else {
eof = true;
}
eof = true;
}
convert_input();
process_interrupts();
started_reading = true;
}
static void convert_input(void)
{
if (abstract_ui || !rbuffer_available(input_buffer)) {
// No input buffer space
return;
}
bool convert = input_conv.vc_type != CONV_NONE;
// Set unconverted data/length
char *data = rbuffer_read_ptr(read_buffer);
size_t data_length = rbuffer_pending(read_buffer);
size_t converted_length = data_length;
if (convert) {
// Perform input conversion according to `input_conv`
size_t unconverted_length = 0;
data = (char *)string_convert_ext(&input_conv,
(uint8_t *)data,
(int *)&converted_length,
(int *)&unconverted_length);
data_length -= unconverted_length;
}
// The conversion code will be gone eventually, for now assume `input_buffer`
// always has space for the converted data(it's many times the size of
// `read_buffer`, so it's hard to imagine a scenario where the converted data
// doesn't fit)
assert(converted_length <= rbuffer_available(input_buffer));
// Write processed data to input buffer.
(void)rbuffer_write(input_buffer, data, converted_length);
// Adjust raw buffer pointers
rbuffer_consumed(read_buffer, data_length);
if (convert) {
// data points to memory allocated by `string_convert_ext`, free it.
free(data);
}
char *buf = rbuffer_read_ptr(read_buffer);
size_t buf_size = rbuffer_pending(read_buffer);
(void)rbuffer_write(input_buffer, buf, buf_size);
rbuffer_consumed(read_buffer, buf_size);
}
static void process_interrupts(void)
@ -440,9 +341,8 @@ static int push_event_key(uint8_t *buf, int maxlen)
static bool input_ready(void)
{
return typebuf_was_filled || // API call filled typeahead
rbuffer_pending(input_buffer) > 0 || // Stdin input
event_has_deferred() || // Events must be processed
(!abstract_ui && eof); // Stdin closed
rbuffer_pending(input_buffer) > 0 || // Input buffer filled
event_has_deferred(); // Events must be processed
}
// Exit because of an input read error.

View File

@ -271,15 +271,6 @@ void rstream_set_file(RStream *rstream, uv_file file)
rstream->free_handle = true;
}
/// Tests if the stream is backed by a regular file
///
/// @param rstream The `RStream` instance
/// @return True if the underlying file descriptor represents a regular file
bool rstream_is_regular_file(RStream *rstream)
{
return rstream->file_type == UV_FILE;
}
/// Starts watching for events from a `RStream` instance.
///
/// @param rstream The `RStream` instance

View File

@ -20,7 +20,7 @@
KMEMPOOL_INIT(SignalEventPool, int, SignalEventFreer)
kmempool_t(SignalEventPool) *signal_event_pool = NULL;
static uv_signal_t sint, spipe, shup, squit, sterm, swinch;
static uv_signal_t spipe, shup, squit, sterm;
#ifdef SIGPWR
static uv_signal_t spwr;
#endif
@ -30,24 +30,18 @@ static bool rejecting_deadly;
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "os/signal.c.generated.h"
#endif
void signal_init(void)
{
signal_event_pool = kmp_init(SignalEventPool);
uv_signal_init(uv_default_loop(), &sint);
uv_signal_init(uv_default_loop(), &spipe);
uv_signal_init(uv_default_loop(), &shup);
uv_signal_init(uv_default_loop(), &squit);
uv_signal_init(uv_default_loop(), &sterm);
uv_signal_init(uv_default_loop(), &swinch);
uv_signal_start(&sint, signal_cb, SIGINT);
uv_signal_start(&spipe, signal_cb, SIGPIPE);
uv_signal_start(&shup, signal_cb, SIGHUP);
uv_signal_start(&squit, signal_cb, SIGQUIT);
uv_signal_start(&sterm, signal_cb, SIGTERM);
if (!abstract_ui) {
// TODO(tarruda): There must be an API function for resizing window
uv_signal_start(&swinch, signal_cb, SIGWINCH);
}
#ifdef SIGPWR
uv_signal_init(uv_default_loop(), &spwr);
uv_signal_start(&spwr, signal_cb, SIGPWR);
@ -57,12 +51,10 @@ void signal_init(void)
void signal_teardown(void)
{
signal_stop();
uv_close((uv_handle_t *)&sint, NULL);
uv_close((uv_handle_t *)&spipe, NULL);
uv_close((uv_handle_t *)&shup, NULL);
uv_close((uv_handle_t *)&squit, NULL);
uv_close((uv_handle_t *)&sterm, NULL);
uv_close((uv_handle_t *)&swinch, NULL);
#ifdef SIGPWR
uv_close((uv_handle_t *)&spwr, NULL);
#endif
@ -70,12 +62,10 @@ void signal_teardown(void)
void signal_stop(void)
{
uv_signal_stop(&sint);
uv_signal_stop(&spipe);
uv_signal_stop(&shup);
uv_signal_stop(&squit);
uv_signal_stop(&sterm);
uv_signal_stop(&swinch);
#ifdef SIGPWR
uv_signal_stop(&spwr);
#endif
@ -94,16 +84,12 @@ void signal_accept_deadly(void)
static char * signal_name(int signum)
{
switch (signum) {
case SIGINT:
return "SIGINT";
#ifdef SIGPWR
case SIGPWR:
return "SIGPWR";
#endif
case SIGPIPE:
return "SIGPIPE";
case SIGWINCH:
return "SIGWINCH";
case SIGTERM:
return "SIGTERM";
case SIGQUIT:
@ -148,9 +134,6 @@ static void on_signal_event(Event event)
kmp_free(SignalEventPool, signal_event_pool, event.data);
switch (signum) {
case SIGINT:
got_int = true;
break;
#ifdef SIGPWR
case SIGPWR:
// Signal of a power failure(eg batteries low), flush the swap files to
@ -161,9 +144,6 @@ static void on_signal_event(Event event)
case SIGPIPE:
// Ignore
break;
case SIGWINCH:
shell_resized();
break;
case SIGTERM:
case SIGQUIT:
case SIGHUP:

View File

@ -56,18 +56,10 @@
#include "nvim/msgpack_rpc/helpers.h"
#include "nvim/msgpack_rpc/defs.h"
#if defined(HAVE_SYS_IOCTL_H)
# include <sys/ioctl.h>
#endif
#ifdef HAVE_STROPTS_H
# include <stropts.h>
#endif
#if defined(HAVE_TERMIOS_H)
# include <termios.h>
#endif
#ifdef HAVE_SELINUX
# include <selinux/selinux.h>
static int selinux_enabled = -1;
@ -82,53 +74,6 @@ static int did_set_title = FALSE;
static char_u *oldicon = NULL;
static int did_set_icon = FALSE;
/*
* If the machine has job control, use it to suspend the program,
* otherwise fake it by starting a new shell.
*/
void mch_suspend(void)
{
#if defined(SIGTSTP)
out_flush(); /* needed to make cursor visible on some systems */
settmode(TMODE_COOK);
out_flush(); /* needed to disable mouse on some systems */
// Note: compiler defines _REENTRANT when given -pthread flag.
# if defined(_REENTRANT) && defined(SIGCONT)
sigcont_received = FALSE;
# endif
uv_kill(0, SIGTSTP); // send ourselves a STOP signal
# if defined(_REENTRANT) && defined(SIGCONT)
/*
* Wait for the SIGCONT signal to be handled. It generally happens
* immediately, but somehow not all the time. Do not call pause()
* because there would be race condition which would hang Vim if
* signal happened in between the test of sigcont_received and the
* call to pause(). If signal is not yet received, call sleep(0)
* to just yield CPU. Signal should then be received. If somehow
* it's still not received, sleep 1, 2, 3 ms. Don't bother waiting
* further if signal is not received after 1+2+3+4 ms (not expected
* to happen).
*/
{
long wait_time;
for (wait_time = 0; !sigcont_received && wait_time <= 3L; wait_time++)
/* Loop is not entered most of the time */
os_delay(wait_time, false);
}
# endif
/*
* Set oldtitle to NULL, so the current title is obtained again.
*/
free(oldtitle);
oldtitle = NULL;
settmode(TMODE_RAW);
need_check_timestamps = TRUE;
did_check_timestamps = FALSE;
#endif
}
static int get_x11_title(int test_only)
{
return FALSE;
@ -161,7 +106,6 @@ int mch_can_restore_icon(void)
*/
void mch_settitle(char_u *title, char_u *icon)
{
int type = 0;
static int recursive = 0;
if (T_NAME == NULL) /* no terminal name (yet) */
@ -175,39 +119,13 @@ void mch_settitle(char_u *title, char_u *icon)
return;
++recursive;
/*
* if the window ID and the display is known, we may use X11 calls
*/
/*
* Note: if "t_ts" is set, title is set with escape sequence rather
* than x11 calls, because the x11 calls don't always work
*/
if ((type || *T_TS != NUL || abstract_ui) && title != NULL) {
if (oldtitle == NULL
) /* first call but not in GUI, save title */
(void)get_x11_title(FALSE);
if (abstract_ui) {
ui_set_title((char *)title);
} else if (*T_TS != NUL) /* it's OK if t_fs is empty */
term_settitle(title);
if (title != NULL) {
ui_set_title((char *)title);
did_set_title = TRUE;
}
if ((type || *T_CIS != NUL || abstract_ui) && icon != NULL) {
if (oldicon == NULL
) /* first call, save icon */
get_x11_icon(FALSE);
if (abstract_ui) {
ui_set_icon((char *)icon);
} else if (*T_CIS != NUL) {
out_str(T_CIS); /* set icon start */
out_str_nf(icon);
out_str(T_CIE); /* set icon end */
out_flush();
}
if (icon != NULL) {
ui_set_icon((char *)icon);
did_set_icon = TRUE;
}
--recursive;
@ -245,17 +163,6 @@ int vim_is_xterm(char_u *name)
|| STRCMP(name, "builtin_xterm") == 0;
}
/*
* Return TRUE if "name" appears to be that of a terminal
* known to support the xterm-style mouse protocol.
* Relies on term_is_xterm having been set to its correct value.
*/
int use_xterm_like_mouse(char_u *name)
{
return name != NULL
&& (term_is_xterm || STRNICMP(name, "screen", 6) == 0);
}
/*
* Return non-zero when using an xterm mouse, according to 'ttymouse'.
* Return 1 for "xterm".
@ -481,7 +388,6 @@ void mch_exit(int r)
exiting = TRUE;
{
settmode(TMODE_COOK);
mch_restore_title(3); /* restore xterm title and icon name */
/*
* When t_ti is not empty but it doesn't cause swapping terminal
@ -492,9 +398,7 @@ void mch_exit(int r)
if (swapping_screen() && !newline_on_exit)
exit_scroll();
/* Stop termcap: May need to check for T_CRV response, which
* requires RAW mode. */
stoptermcap();
ui_builtin_stop();
/*
* A newline is only required after a message in the alternate screen.
@ -524,143 +428,6 @@ void mch_exit(int r)
exit(r);
}
void mch_settmode(int tmode)
{
static int first = TRUE;
#if defined(ECHOE) && defined(ICANON) && (defined(HAVE_TERMIO_H) || \
defined(HAVE_TERMIOS_H))
/*
* for "new" tty systems
*/
# ifdef HAVE_TERMIOS_H
static struct termios told;
struct termios tnew;
# else
static struct termio told;
struct termio tnew;
# endif
if (first) {
first = FALSE;
# if defined(HAVE_TERMIOS_H)
tcgetattr(read_cmd_fd, &told);
# else
ioctl(read_cmd_fd, TCGETA, &told);
# endif
}
tnew = told;
if (tmode == TMODE_RAW) {
/*
* ~ICRNL enables typing ^V^M
*/
tnew.c_iflag &= ~ICRNL;
tnew.c_lflag &= ~(ICANON | ECHO | ISIG | ECHOE
# if defined(IEXTEN)
| IEXTEN /* IEXTEN enables typing ^V on SOLARIS */
# endif
);
# ifdef ONLCR /* don't map NL -> CR NL, we do it ourselves */
tnew.c_oflag &= ~ONLCR;
# endif
tnew.c_cc[VMIN] = 1; /* return after 1 char */
tnew.c_cc[VTIME] = 0; /* don't wait */
} else if (tmode == TMODE_SLEEP)
tnew.c_lflag &= ~(ECHO);
# if defined(HAVE_TERMIOS_H)
{
int n = 10;
/* A signal may cause tcsetattr() to fail (e.g., SIGCONT). Retry a
* few times. */
while (tcsetattr(read_cmd_fd, TCSANOW, &tnew) == -1
&& errno == EINTR && n > 0)
--n;
}
# else
ioctl(read_cmd_fd, TCSETA, &tnew);
# endif
#else
/*
* for "old" tty systems
*/
# ifndef TIOCSETN
# define TIOCSETN TIOCSETP /* for hpux 9.0 */
# endif
static struct sgttyb ttybold;
struct sgttyb ttybnew;
if (first) {
first = FALSE;
ioctl(read_cmd_fd, TIOCGETP, &ttybold);
}
ttybnew = ttybold;
if (tmode == TMODE_RAW) {
ttybnew.sg_flags &= ~(CRMOD | ECHO);
ttybnew.sg_flags |= RAW;
} else if (tmode == TMODE_SLEEP)
ttybnew.sg_flags &= ~(ECHO);
ioctl(read_cmd_fd, TIOCSETN, &ttybnew);
#endif
curr_tmode = tmode;
}
/*
* Try to get the code for "t_kb" from the stty setting
*
* Even if termcap claims a backspace key, the user's setting *should*
* prevail. stty knows more about reality than termcap does, and if
* somebody's usual erase key is DEL (which, for most BSD users, it will
* be), they're going to get really annoyed if their erase key starts
* doing forward deletes for no reason. (Eric Fischer)
*/
void get_stty(void)
{
char_u buf[2];
char_u *p;
#if defined(ECHOE) && defined(ICANON) && (defined(HAVE_TERMIO_H) || \
defined(HAVE_TERMIOS_H))
/* for "new" tty systems */
# ifdef HAVE_TERMIOS_H
struct termios keys;
# else
struct termio keys;
# endif
# if defined(HAVE_TERMIOS_H)
if (tcgetattr(read_cmd_fd, &keys) != -1)
# else
if (ioctl(read_cmd_fd, TCGETA, &keys) != -1)
# endif
{
buf[0] = keys.c_cc[VERASE];
intr_char = keys.c_cc[VINTR];
#else
/* for "old" tty systems */
struct sgttyb keys;
if (ioctl(read_cmd_fd, TIOCGETP, &keys) != -1) {
buf[0] = keys.sg_erase;
intr_char = keys.sg_kill;
#endif
buf[1] = NUL;
add_termcode((char_u *)"kb", buf, FALSE);
/*
* If <BS> and <DEL> are now the same, redefine <DEL>.
*/
p = find_termcode((char_u *)"kD");
if (p != NULL && p[0] == buf[0] && p[1] == buf[1])
do_fixdel(NULL);
}
}
/*
* Set mouse clicks on or off.
*/
@ -712,173 +479,6 @@ void mch_setmouse(int on)
}
/// Sets the mouse termcode, depending on the 'term' and 'ttymouse' options.
void check_mouse_termcode(void)
{
xterm_conflict_mouse = false;
if (use_xterm_mouse()
&& use_xterm_mouse() != 3
) {
set_mouse_termcode(KS_MOUSE, (char_u *)(term_is_8bit(T_NAME)
? "\233M"
: "\033[M"));
if (*p_mouse != NUL) {
/* force mouse off and maybe on to send possibly new mouse
* activation sequence to the xterm, with(out) drag tracing. */
mch_setmouse(FALSE);
setmouse();
}
} else
del_mouse_termcode(KS_MOUSE);
/* There is no conflict, but one may type "ESC }" from Insert mode. Don't
* define it in the GUI or when using an xterm. */
if (!use_xterm_mouse()
)
set_mouse_termcode(KS_NETTERM_MOUSE,
(char_u *)"\033}");
else
del_mouse_termcode(KS_NETTERM_MOUSE);
// Conflicts with xterm mouse: "\033[" and "\033[M".
// Also conflicts with the xterm termresponse, skip this if it was requested
// already.
if (!use_xterm_mouse()) {
set_mouse_termcode(KS_DEC_MOUSE, (char_u *)(term_is_8bit(T_NAME)
? "\233" : "\033["));
xterm_conflict_mouse = true;
}
else {
del_mouse_termcode(KS_DEC_MOUSE);
}
/* same as the dec mouse */
if (use_xterm_mouse() == 3 && !did_request_esc_sequence()) {
set_mouse_termcode(KS_URXVT_MOUSE,
(char_u *)(term_is_8bit(T_NAME) ? "\233" : "\033["));
if (*p_mouse != NUL) {
mch_setmouse(false);
setmouse();
}
resume_get_esc_sequence();
} else {
del_mouse_termcode(KS_URXVT_MOUSE);
}
// There is no conflict with xterm mouse.
if (use_xterm_mouse() == 4) {
set_mouse_termcode(KS_SGR_MOUSE, (char_u *)(term_is_8bit(T_NAME)
? "\233<"
: "\033[<"));
if (*p_mouse != NUL) {
mch_setmouse(FALSE);
setmouse();
}
} else {
del_mouse_termcode(KS_SGR_MOUSE);
}
}
/*
* Try to get the current window size:
* 1. with an ioctl(), most accurate method
* 2. from the environment variables LINES and COLUMNS
* 3. from the termcap
* 4. keep using the old values
* Return OK when size could be determined, FAIL otherwise.
*/
int mch_get_shellsize(void)
{
long rows = 0;
long columns = 0;
char_u *p;
/*
* 1. try using an ioctl. It is the most accurate method.
*
* Try using TIOCGWINSZ first, some systems that have it also define
* TIOCGSIZE but don't have a struct ttysize.
*/
# ifdef TIOCGWINSZ
{
struct winsize ws;
int fd = 1;
/* When stdout is not a tty, use stdin for the ioctl(). */
if (!isatty(fd) && isatty(read_cmd_fd))
fd = read_cmd_fd;
if (ioctl(fd, TIOCGWINSZ, &ws) == 0) {
columns = ws.ws_col;
rows = ws.ws_row;
}
}
# else /* TIOCGWINSZ */
# ifdef TIOCGSIZE
{
struct ttysize ts;
int fd = 1;
/* When stdout is not a tty, use stdin for the ioctl(). */
if (!isatty(fd) && isatty(read_cmd_fd))
fd = read_cmd_fd;
if (ioctl(fd, TIOCGSIZE, &ts) == 0) {
columns = ts.ts_cols;
rows = ts.ts_lines;
}
}
# endif /* TIOCGSIZE */
# endif /* TIOCGWINSZ */
/*
* 2. get size from environment
* When being POSIX compliant ('|' flag in 'cpoptions') this overrules
* the ioctl() values!
*/
if (columns == 0 || rows == 0 || vim_strchr(p_cpo, CPO_TSIZE) != NULL) {
if ((p = (char_u *)os_getenv("LINES")))
rows = atoi((char *)p);
if ((p = (char_u *)os_getenv("COLUMNS")))
columns = atoi((char *)p);
}
#ifdef HAVE_TGETENT
/*
* 3. try reading "co" and "li" entries from termcap
*/
if (columns == 0 || rows == 0)
getlinecol(&columns, &rows);
#endif
/*
* 4. If everything fails, use the old values
*/
if (columns <= 0 || rows <= 0)
return FAIL;
Rows = rows;
Columns = columns;
limit_screen_size();
return OK;
}
/*
* Try to set the window size to Rows and Columns.
*/
void mch_set_shellsize(void)
{
if (*T_CWS) {
/*
* NOTE: if you get an error here that term_set_winsize() is
* undefined, check the output of configure. It could probably not
* find a ncurses, termcap or termlib library.
*/
term_set_winsize((int)Rows, (int)Columns);
out_flush();
screen_start(); /* don't know where cursor is now */
}
}
/*
* mch_expand_wildcards() - this code does wild-card pattern matching using
* the shell

View File

@ -231,119 +231,6 @@ void redraw_buf_later(buf_T *buf, int type)
}
}
/*
* Redraw as soon as possible. When the command line is not scrolled redraw
* right away and restore what was on the command line.
* Return a code indicating what happened.
*/
int redraw_asap(int type)
{
int rows;
int r;
int ret = 0;
schar_T *screenline; /* copy from ScreenLines[] */
sattr_T *screenattr; /* copy from ScreenAttrs[] */
int i;
u8char_T *screenlineUC = NULL; /* copy from ScreenLinesUC[] */
u8char_T *screenlineC[MAX_MCO]; /* copy from ScreenLinesC[][] */
schar_T *screenline2 = NULL; /* copy from ScreenLines2[] */
const bool l_enc_utf8 = enc_utf8;
const int l_enc_dbcs = enc_dbcs;
const long l_p_mco = p_mco;
redraw_later(type);
if (msg_scrolled || (State != NORMAL && State != NORMAL_BUSY))
return ret;
/* Allocate space to save the text displayed in the command line area. */
rows = Rows - cmdline_row;
screenline = xmalloc((size_t)(rows * Columns * sizeof(schar_T)));
screenattr = xmalloc((size_t)(rows * Columns * sizeof(sattr_T)));
if (l_enc_utf8) {
screenlineUC = xmalloc((size_t)(rows * Columns * sizeof(u8char_T)));
for (i = 0; i < l_p_mco; ++i) {
screenlineC[i] = xmalloc((size_t)(rows * Columns * sizeof(u8char_T)));
}
}
if (l_enc_dbcs == DBCS_JPNU) {
screenline2 = xmalloc((size_t)(rows * Columns * sizeof(schar_T)));
}
/* Save the text displayed in the command line area. */
for (r = 0; r < rows; ++r) {
memmove(screenline + r * Columns,
ScreenLines + LineOffset[cmdline_row + r],
(size_t)Columns * sizeof(schar_T));
memmove(screenattr + r * Columns,
ScreenAttrs + LineOffset[cmdline_row + r],
(size_t)Columns * sizeof(sattr_T));
if (l_enc_utf8) {
memmove(screenlineUC + r * Columns,
ScreenLinesUC + LineOffset[cmdline_row + r],
(size_t)Columns * sizeof(u8char_T));
for (i = 0; i < l_p_mco; ++i)
memmove(screenlineC[i] + r * Columns,
ScreenLinesC[r] + LineOffset[cmdline_row + r],
(size_t)Columns * sizeof(u8char_T));
}
if (l_enc_dbcs == DBCS_JPNU)
memmove(screenline2 + r * Columns,
ScreenLines2 + LineOffset[cmdline_row + r],
(size_t)Columns * sizeof(schar_T));
}
update_screen(0);
ret = 3;
if (must_redraw == 0) {
int off = (int)(current_ScreenLine - ScreenLines);
/* Restore the text displayed in the command line area. */
for (r = 0; r < rows; ++r) {
memmove(current_ScreenLine,
screenline + r * Columns,
(size_t)Columns * sizeof(schar_T));
memmove(ScreenAttrs + off,
screenattr + r * Columns,
(size_t)Columns * sizeof(sattr_T));
if (l_enc_utf8) {
memmove(ScreenLinesUC + off,
screenlineUC + r * Columns,
(size_t)Columns * sizeof(u8char_T));
for (i = 0; i < l_p_mco; ++i)
memmove(ScreenLinesC[i] + off,
screenlineC[i] + r * Columns,
(size_t)Columns * sizeof(u8char_T));
}
if (l_enc_dbcs == DBCS_JPNU)
memmove(ScreenLines2 + off,
screenline2 + r * Columns,
(size_t)Columns * sizeof(schar_T));
SCREEN_LINE(cmdline_row + r, 0, Columns, Columns, FALSE);
}
ret = 4;
}
free(screenline);
free(screenattr);
if (l_enc_utf8) {
free(screenlineUC);
for (i = 0; i < l_p_mco; ++i)
free(screenlineC[i]);
}
if (l_enc_dbcs == DBCS_JPNU)
free(screenline2);
/* Show the intro message when appropriate. */
maybe_intro_message();
setcursor();
return ret;
}
/*
* Changed something in the current window, at buffer line "lnum", that
* requires that line and possibly other lines to be redrawn.
@ -4472,23 +4359,6 @@ static void screen_line(int row, int coloff, int endcol, int clear_width, int rl
if (char_cells == 2)
ScreenLines[off_to + 1] = ScreenLines[off_from + 1];
#if defined(FEAT_GUI) || defined(UNIX)
/* The bold trick makes a single column of pixels appear in the
* next character. When a bold character is removed, the next
* character should be redrawn too. This happens for our own GUI
* and for some xterms. */
if (
# ifdef UNIX
term_is_xterm
# endif
) {
hl = ScreenAttrs[off_to];
if (hl > HL_ALL)
hl = syn_attr2attr(hl);
if (hl & HL_BOLD)
redraw_next = TRUE;
}
#endif
ScreenAttrs[off_to] = ScreenAttrs[off_from];
/* For simplicity set the attributes of second half of a
* double-wide character equal to the first half. */
@ -5456,24 +5326,6 @@ void screen_puts_len(char_u *text, int textlen, int row, int col, int attr)
if (need_redraw
|| force_redraw_this
) {
#if defined(FEAT_GUI) || defined(UNIX)
/* The bold trick makes a single row of pixels appear in the next
* character. When a bold character is removed, the next
* character should be redrawn too. This happens for our own GUI
* and for some xterms. */
if (need_redraw && ScreenLines[off] != ' ' && (
# ifdef UNIX
term_is_xterm
# endif
)) {
int n = ScreenAttrs[off];
if (n > HL_ALL)
n = syn_attr2attr(n);
if (n & HL_BOLD)
force_redraw_next = TRUE;
}
#endif
/* When at the end of the text and overwriting a two-cell
* character with a one-cell character, need to clear the next
* cell. Also when overwriting the left halve of a two-cell char
@ -5837,156 +5689,25 @@ next_search_hl_pos(
static void screen_start_highlight(int attr)
{
attrentry_T *aep = NULL;
screen_attr = attr;
if (full_screen) {
if (abstract_ui) {
char buf[20];
sprintf(buf, "\033|%dh", attr);
OUT_STR(buf);
} else {
if (attr > HL_ALL) { /* special HL attr. */
if (t_colors > 1)
aep = syn_cterm_attr2entry(attr);
else
aep = syn_term_attr2entry(attr);
if (aep == NULL) /* did ":syntax clear" */
attr = 0;
else
attr = aep->ae_attr;
}
if ((attr & HL_BOLD) && T_MD != NULL) /* bold */
out_str(T_MD);
else if (aep != NULL && t_colors > 1 && aep->ae_u.cterm.fg_color
&& cterm_normal_fg_bold)
/* If the Normal FG color has BOLD attribute and the new HL
* has a FG color defined, clear BOLD. */
out_str(T_ME);
if ((attr & HL_STANDOUT) && T_SO != NULL) /* standout */
out_str(T_SO);
if ((attr & (HL_UNDERLINE | HL_UNDERCURL)) && T_US != NULL)
/* underline or undercurl */
out_str(T_US);
if ((attr & HL_ITALIC) && T_CZH != NULL) /* italic */
out_str(T_CZH);
if ((attr & HL_INVERSE) && T_MR != NULL) /* inverse (reverse) */
out_str(T_MR);
/*
* Output the color or start string after bold etc., in case the
* bold etc. override the color setting.
*/
if (aep != NULL) {
if (t_colors > 1) {
if (aep->ae_u.cterm.fg_color)
term_fg_color(aep->ae_u.cterm.fg_color - 1);
if (aep->ae_u.cterm.bg_color)
term_bg_color(aep->ae_u.cterm.bg_color - 1);
} else {
if (aep->ae_u.term.start != NULL)
out_str(aep->ae_u.term.start);
}
}
}
char buf[20];
sprintf(buf, "\033|%dh", attr);
OUT_STR(buf);
}
}
void screen_stop_highlight(void)
{
int do_ME = FALSE; /* output T_ME code */
if (screen_attr != 0) {
if (abstract_ui) {
// Handled in ui.c
char buf[20];
sprintf(buf, "\033|%dH", screen_attr);
OUT_STR(buf);
} else {
if (screen_attr > HL_ALL) { /* special HL attr. */
attrentry_T *aep;
if (t_colors > 1) {
/*
* Assume that t_me restores the original colors!
*/
aep = syn_cterm_attr2entry(screen_attr);
if (aep != NULL && (aep->ae_u.cterm.fg_color
|| aep->ae_u.cterm.bg_color))
do_ME = TRUE;
} else {
aep = syn_term_attr2entry(screen_attr);
if (aep != NULL && aep->ae_u.term.stop != NULL) {
if (STRCMP(aep->ae_u.term.stop, T_ME) == 0)
do_ME = TRUE;
else
out_str(aep->ae_u.term.stop);
}
}
if (aep == NULL) /* did ":syntax clear" */
screen_attr = 0;
else
screen_attr = aep->ae_attr;
}
/*
* Often all ending-codes are equal to T_ME. Avoid outputting the
* same sequence several times.
*/
if (screen_attr & HL_STANDOUT) {
if (STRCMP(T_SE, T_ME) == 0)
do_ME = TRUE;
else
out_str(T_SE);
}
if (screen_attr & (HL_UNDERLINE | HL_UNDERCURL)) {
if (STRCMP(T_UE, T_ME) == 0)
do_ME = TRUE;
else
out_str(T_UE);
}
if (screen_attr & HL_ITALIC) {
if (STRCMP(T_CZR, T_ME) == 0)
do_ME = TRUE;
else
out_str(T_CZR);
}
if (do_ME || (screen_attr & (HL_BOLD | HL_INVERSE)))
out_str(T_ME);
if (t_colors > 1) {
/* set Normal cterm colors */
if (cterm_normal_fg_color != 0)
term_fg_color(cterm_normal_fg_color - 1);
if (cterm_normal_bg_color != 0)
term_bg_color(cterm_normal_bg_color - 1);
if (cterm_normal_fg_bold)
out_str(T_MD);
}
}
// Handled in ui.c
char buf[20];
sprintf(buf, "\033|%dH", screen_attr);
OUT_STR(buf);
}
screen_attr = 0;
}
/*
* Reset the colors for a cterm. Used when leaving Vim.
* The machine specific code may override this again.
*/
void reset_cterm_colors(void)
{
if (!abstract_ui && t_colors > 1) {
/* set Normal cterm colors */
if (cterm_normal_fg_color > 0 || cterm_normal_bg_color > 0) {
out_str(T_OP);
screen_attr = -1;
}
if (cterm_normal_fg_bold) {
out_str(T_ME);
screen_attr = -1;
}
}
}
/*
* Put character ScreenLines["off"] on the screen at position "row" and "col",
* using the attributes from ScreenAttrs["off"].
@ -6138,7 +5859,6 @@ void screen_fill(int start_row, int end_row, int start_col, int end_col, int c1,
int end_off;
int did_delete;
int c;
int norm_term;
#if defined(FEAT_GUI) || defined(UNIX)
int force_next = FALSE;
#endif
@ -6153,7 +5873,6 @@ void screen_fill(int start_row, int end_row, int start_col, int end_col, int c1,
return;
/* it's a "normal" terminal when not in a GUI or cterm */
norm_term = (!abstract_ui && t_colors <= 1);
for (row = start_row; row < end_row; ++row) {
if (has_mbyte
) {
@ -6174,11 +5893,7 @@ void screen_fill(int start_row, int end_row, int start_col, int end_col, int c1,
did_delete = FALSE;
if (c2 == ' '
&& end_col == Columns
&& can_clear(T_CE)
&& (attr == 0
|| (norm_term
&& attr <= HL_ALL
&& ((attr & ~(HL_BOLD | HL_ITALIC)) == 0)))) {
&& attr == 0) {
/*
* check if we really need to clear something
*/
@ -6227,24 +5942,6 @@ void screen_fill(int start_row, int end_row, int start_col, int end_col, int c1,
|| force_next
#endif
) {
#if defined(FEAT_GUI) || defined(UNIX)
/* The bold trick may make a single row of pixels appear in
* the next character. When a bold character is removed, the
* next character should be redrawn too. This happens for our
* own GUI and for some xterms. */
if (
# ifdef UNIX
term_is_xterm
# endif
) {
if (ScreenLines[off] != ' '
&& (ScreenAttrs[off] > HL_ALL
|| ScreenAttrs[off] & HL_BOLD))
force_next = TRUE;
else
force_next = FALSE;
}
#endif
ScreenLines[off] = c;
if (enc_utf8) {
if (c >= 0x80) {
@ -6584,10 +6281,6 @@ static void screenclear2(void)
return;
}
if (!abstract_ui) {
screen_attr = -1; /* force setting the Normal colors */
}
screen_stop_highlight(); /* don't want highlighting here */
@ -6597,17 +6290,9 @@ static void screenclear2(void)
LineWraps[i] = FALSE;
}
if (can_clear(T_CL)) {
out_str(T_CL); /* clear the display */
clear_cmdline = FALSE;
mode_displayed = FALSE;
} else {
/* can't clear the screen, mark all chars with invalid attributes */
for (i = 0; i < Rows; ++i)
lineinvalid(LineOffset[i], (int)Columns);
clear_cmdline = TRUE;
}
out_str(T_CL); /* clear the display */
clear_cmdline = FALSE;
mode_displayed = FALSE;
screen_cleared = TRUE; /* can use contents of ScreenLines now */
win_rest_invalid(firstwin);
@ -6636,15 +6321,6 @@ static void lineclear(unsigned off, int width)
(void)memset(ScreenAttrs + off, 0, (size_t)width * sizeof(sattr_T));
}
/*
* Mark one line in ScreenLines invalid by setting the attributes to an
* invalid value.
*/
static void lineinvalid(unsigned off, int width)
{
(void)memset(ScreenAttrs + off, -1, (size_t)width * sizeof(sattr_T));
}
/*
* Copy part of a Screenline for vertically split window "wp".
*/
@ -6671,16 +6347,6 @@ static void linecopy(int to, int from, win_T *wp)
wp->w_width * sizeof(sattr_T));
}
/*
* Return TRUE if clearing with term string "p" would work.
* It can't work when the string is empty or it won't set the right background.
*/
int can_clear(char_u *p)
{
return abstract_ui || (*p != NUL && (t_colors <= 1
|| cterm_normal_bg_color == 0 || *T_UT != NUL));
}
/*
* Reset cursor position. Use whenever cursor was moved because of outputting
* something directly to the screen (shell commands) or a terminal control
@ -7167,7 +6833,7 @@ screen_ins_lines (
int cursor_row;
int type;
int result_empty;
int can_ce = can_clear(T_CE);
int can_ce = true;
/*
* FAIL if
@ -7207,7 +6873,7 @@ screen_ins_lines (
result_empty = (row + line_count >= end);
if (wp != NULL && wp->w_width != Columns && *T_CSV == NUL)
type = USE_REDRAW;
else if (can_clear(T_CD) && result_empty)
else if (result_empty)
type = USE_T_CD;
else if (*T_CAL != NUL && (line_count > 1 || *T_AL == NUL))
type = USE_T_CAL;
@ -7260,10 +6926,7 @@ screen_ins_lines (
while ((j -= line_count) >= row)
linecopy(j + line_count, j, wp);
j += line_count;
if (can_clear((char_u *)" "))
lineclear(LineOffset[j] + wp->w_wincol, wp->w_width);
else
lineinvalid(LineOffset[j] + wp->w_wincol, wp->w_width);
lineclear(LineOffset[j] + wp->w_wincol, wp->w_width);
LineWraps[j] = FALSE;
} else {
j = end - 1 - i;
@ -7274,10 +6937,7 @@ screen_ins_lines (
}
LineOffset[j + line_count] = temp;
LineWraps[j + line_count] = FALSE;
if (can_clear((char_u *)" "))
lineclear(temp, (int)Columns);
else
lineinvalid(temp, (int)Columns);
lineclear(temp, (int)Columns);
}
}
@ -7364,7 +7024,7 @@ screen_del_lines (
* We can delete lines only when 'db' flag not set or when 'ce' option
* available.
*/
can_delete = (*T_DB == NUL || can_clear(T_CE));
can_delete = true;
/*
* There are six ways to delete lines:
@ -7380,7 +7040,7 @@ screen_del_lines (
*/
if (wp != NULL && wp->w_width != Columns && *T_CSV == NUL)
type = USE_REDRAW;
else if (can_clear(T_CD) && result_empty)
else if (result_empty)
type = USE_T_CD;
else if (row == 0 && (
/* On the Amiga, somehow '\n' on the last line doesn't always scroll
@ -7390,9 +7050,7 @@ screen_del_lines (
type = USE_NL;
else if (*T_CDL != NUL && line_count > 1 && can_delete)
type = USE_T_CDL;
else if (can_clear(T_CE) && result_empty
&& (wp == NULL || wp->w_width == Columns)
)
else if (result_empty && (wp == NULL || wp->w_width == Columns))
type = USE_T_CE;
else if (*T_DL != NUL && can_delete)
type = USE_T_DL;
@ -7424,10 +7082,7 @@ screen_del_lines (
while ((j += line_count) <= end - 1)
linecopy(j - line_count, j, wp);
j -= line_count;
if (can_clear((char_u *)" "))
lineclear(LineOffset[j] + wp->w_wincol, wp->w_width);
else
lineinvalid(LineOffset[j] + wp->w_wincol, wp->w_width);
lineclear(LineOffset[j] + wp->w_wincol, wp->w_width);
LineWraps[j] = FALSE;
} else {
/* whole width, moving the line pointers is faster */
@ -7439,10 +7094,7 @@ screen_del_lines (
}
LineOffset[j - line_count] = temp;
LineWraps[j - line_count] = FALSE;
if (can_clear((char_u *)" "))
lineclear(temp, (int)Columns);
else
lineinvalid(temp, (int)Columns);
lineclear(temp, (int)Columns);
}
}
@ -8153,7 +7805,7 @@ int screen_screenrow(void)
* If 'mustset' is FALSE, we may try to get the real window size and if
* it fails use 'width' and 'height'.
*/
void screen_resize(int width, int height, int mustset)
void screen_resize(int width, int height)
{
static int busy = FALSE;
@ -8182,24 +7834,15 @@ void screen_resize(int width, int height, int mustset)
++busy;
// TODO(tarruda): "mustset" is still used in the old tests, which don't use
// "abstract_ui" yet. This will change when a new TUI is merged.
if (abstract_ui || mustset || (ui_get_shellsize() == FAIL && height != 0)) {
Rows = height;
Columns = width;
}
Rows = height;
Columns = width;
check_shellsize();
height = Rows;
width = Columns;
if (abstract_ui) {
// Clear the output buffer to ensure UIs don't receive redraw command meant
// for invalid screen sizes.
out_buf_clear();
ui_resize(width, height);
} else {
mch_set_shellsize();
}
// Clear the output buffer to ensure UIs don't receive redraw command meant
// for invalid screen sizes.
out_buf_clear();
ui_resize(width, height);
/* The window layout used to be adjusted here, but it now happens in
* screenalloc() (also invoked from screenclear()). That is because the

View File

@ -351,40 +351,3 @@ bool sha256_self_test(void)
}
return failures == false;
}
/// Fill "header[header_len]" with random_data.
/// Also "salt[salt_len]" when "salt" is not NULL.
///
/// @param header
/// @param header_len
/// @param salt
/// @param salt_len
void sha2_seed(char_u *restrict header, size_t header_len,
char_u *restrict salt, size_t salt_len)
{
static char_u random_data[1000];
char_u sha256sum[SHA256_SUM_SIZE];
context_sha256_T ctx;
unsigned int seed = (unsigned int) os_hrtime();
size_t i;
for (i = 0; i < sizeof(random_data) - 1; i++) {
random_data[i] = (char_u) ((os_hrtime() ^ (uint64_t)rand_r(&seed)) & 0xff);
}
sha256_start(&ctx);
sha256_update(&ctx, random_data, sizeof(random_data));
sha256_finish(&ctx, sha256sum);
// put first block into header.
for (i = 0; i < header_len; i++) {
header[i] = sha256sum[i % sizeof(sha256sum)];
}
// put remaining block into salt.
if (salt != NULL) {
for (i = 0; i < salt_len; i++) {
salt[i] = sha256sum[(i + header_len) % sizeof(sha256sum)];
}
}
}

View File

@ -300,17 +300,6 @@ void del_trailing_spaces(char_u *ptr)
*q = NUL;
}
/*
* Like strncpy(), but always terminate the result with one NUL.
* "to" must be "len + 1" long!
*/
void vim_strncpy(char_u *restrict to, const char_u *restrict from, size_t len)
FUNC_ATTR_NONNULL_ALL
{
STRNCPY(to, from, len);
to[len] = NUL;
}
/*
* Like strcat(), but make sure the result fits in "tosize" bytes and is
* always NUL terminated.

View File

@ -49,40 +49,32 @@
#include "nvim/os/os.h"
#include "nvim/os/time.h"
/*
* Structure that stores information about a highlight group.
* The ID of a highlight group is also called group ID. It is the index in
* the highlight_ga array PLUS ONE.
*/
// Structure that stores information about a highlight group.
// The ID of a highlight group is also called group ID. It is the index in
// the highlight_ga array PLUS ONE.
struct hl_group {
char_u *sg_name; /* highlight group name */
char_u *sg_name_u; /* uppercase of sg_name */
/* for normal terminals */
int sg_term; /* "term=" highlighting attributes */
char_u *sg_start; /* terminal string for start highl */
char_u *sg_stop; /* terminal string for stop highl */
int sg_term_attr; /* Screen attr for term mode */
/* for color terminals */
int sg_cterm; /* "cterm=" highlighting attr */
int sg_cterm_bold; /* bold attr was set for light color */
int sg_cterm_fg; /* terminal fg color number + 1 */
int sg_cterm_bg; /* terminal bg color number + 1 */
int sg_cterm_attr; /* Screen attr for color term mode */
/* Store the sp color name for the GUI or synIDattr() */
int sg_gui; /* "gui=" highlighting attributes */
char_u *sg_name; // highlight group name
char_u *sg_name_u; // uppercase of sg_name
int sg_attr; // Screen attr
int sg_link; // link to this highlight group ID
int sg_set; // combination of SG_* flags
scid_T sg_scriptID; // script in which the group was last set
// for terminal UIs
int sg_cterm; // "cterm=" highlighting attr
int sg_cterm_fg; // terminal fg color number + 1
int sg_cterm_bg; // terminal bg color number + 1
int sg_cterm_bold; // bold attr was set for light color
// for RGB UIs
int sg_gui; // "gui=" highlighting attributes
RgbValue sg_rgb_fg; // RGB foreground color
RgbValue sg_rgb_bg; // RGB background color
uint8_t *sg_rgb_fg_name; // RGB foreground color name
uint8_t *sg_rgb_bg_name; // RGB background color name
int sg_link; /* link to this highlight group ID */
int sg_set; /* combination of SG_* flags */
scid_T sg_scriptID; /* script in which the group was last set */
};
#define SG_TERM 1 /* term has been set */
#define SG_CTERM 2 /* cterm has been set */
#define SG_GUI 4 /* gui has been set */
#define SG_LINK 8 /* link has been set */
#define SG_CTERM 2 // cterm has been set
#define SG_GUI 4 // gui has been set
#define SG_LINK 8 // link has been set
// highlight groups for 'highlight' option
static garray_T highlight_ga = GA_EMPTY_INIT_VALUE;
@ -221,7 +213,7 @@ struct name_list {
/*
* An attribute number is the index in attr_table plus ATTR_OFF.
*/
#define ATTR_OFF (HL_ALL + 1)
#define ATTR_OFF 1
static char *(spo_name_tab[SPO_COUNT]) =
@ -6054,7 +6046,6 @@ do_highlight (
)
{
char_u *name_end;
char_u *p;
char_u *linep;
char_u *key_start;
char_u *arg_start;
@ -6242,7 +6233,7 @@ do_highlight (
if (STRCMP(key, "NONE") == 0) {
if (!init || HL_TABLE()[idx].sg_set == 0) {
if (!init)
HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
HL_TABLE()[idx].sg_set |= SG_CTERM+SG_GUI;
highlight_clear(idx);
}
continue;
@ -6312,20 +6303,14 @@ do_highlight (
}
if (error)
break;
if (*key == 'T') {
if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM)) {
if (!init)
HL_TABLE()[idx].sg_set |= SG_TERM;
HL_TABLE()[idx].sg_term = attr;
}
} else if (*key == 'C') {
if (*key == 'C') {
if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM)) {
if (!init)
HL_TABLE()[idx].sg_set |= SG_CTERM;
HL_TABLE()[idx].sg_cterm = attr;
HL_TABLE()[idx].sg_cterm_bold = FALSE;
}
} else {
} else if (*key == 'G') {
if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) {
if (!init)
HL_TABLE()[idx].sg_set |= SG_GUI;
@ -6438,27 +6423,17 @@ do_highlight (
}
color &= 7; /* truncate to 8 colors */
} else if (t_colors == 16 || t_colors == 88 || t_colors == 256) {
/*
* Guess: if the termcap entry ends in 'm', it is
* probably an xterm-like terminal. Use the changed
* order for colors.
*/
if (*T_CAF != NUL)
p = T_CAF;
else
p = T_CSF;
if (abstract_ui || (*p != NUL && *(p + STRLEN(p) - 1) == 'm'))
switch (t_colors) {
case 16:
color = color_numbers_8[i];
break;
case 88:
color = color_numbers_88[i];
break;
case 256:
color = color_numbers_256[i];
break;
}
switch (t_colors) {
case 16:
color = color_numbers_8[i];
break;
case 88:
color = color_numbers_88[i];
break;
case 256:
color = color_numbers_256[i];
break;
}
}
}
}
@ -6471,8 +6446,6 @@ do_highlight (
cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
{
must_redraw = CLEAR;
if (termcap_active && color >= 0)
term_fg_color(color);
}
}
} else {
@ -6482,8 +6455,6 @@ do_highlight (
{
must_redraw = CLEAR;
if (color >= 0) {
if (termcap_active)
term_bg_color(color);
if (t_colors < 16)
i = (color == 0 || color == 4);
else
@ -6536,73 +6507,9 @@ do_highlight (
normal_bg = HL_TABLE()[idx].sg_rgb_bg;
}
} else if (STRCMP(key, "GUISP") == 0) {
// Ignored
// Ignored for now
} else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0) {
char_u buf[100];
char_u *tname;
if (!init)
HL_TABLE()[idx].sg_set |= SG_TERM;
/*
* The "start" and "stop" arguments can be a literal escape
* sequence, or a comma separated list of terminal codes.
*/
if (STRNCMP(arg, "t_", 2) == 0) {
off = 0;
buf[0] = 0;
while (arg[off] != NUL) {
/* Isolate one termcap name */
for (len = 0; arg[off + len] &&
arg[off + len] != ','; ++len)
;
tname = vim_strnsave(arg + off, len);
/* lookup the escape sequence for the item */
p = get_term_code(tname);
free(tname);
if (p == NULL) /* ignore non-existing things */
p = (char_u *)"";
/* Append it to the already found stuff */
if ((int)(STRLEN(buf) + STRLEN(p)) >= 99) {
EMSG2(_("E422: terminal code too long: %s"), arg);
error = TRUE;
break;
}
STRCAT(buf, p);
/* Advance to the next item */
off += len;
if (arg[off] == ',') /* another one follows */
++off;
}
} else {
/*
* Copy characters from arg[] to buf[], translating <> codes.
*/
for (p = arg, off = 0; off < 100 - 6 && *p; ) {
len = (int)trans_special(&p, buf + off, FALSE);
if (len > 0) /* recognized special char */
off += len;
else /* copy as normal char */
buf[off++] = *p++;
}
buf[off] = NUL;
}
if (error)
break;
if (STRCMP(buf, "NONE") == 0) /* resetting the value */
p = NULL;
else
p = vim_strsave(buf);
if (key[2] == 'A') {
free(HL_TABLE()[idx].sg_start);
HL_TABLE()[idx].sg_start = p;
} else {
free(HL_TABLE()[idx].sg_stop);
HL_TABLE()[idx].sg_stop = p;
}
// Ignored for now
} else {
EMSG2(_("E423: Illegal argument: %s"), key_start);
error = TRUE;
@ -6628,12 +6535,9 @@ do_highlight (
syn_unadd_group();
else {
if (is_normal_group) {
HL_TABLE()[idx].sg_term_attr = 0;
HL_TABLE()[idx].sg_cterm_attr = 0;
if (abstract_ui) {
// If the normal group has changed, it is simpler to refresh every UI
ui_refresh();
}
HL_TABLE()[idx].sg_attr = 0;
// If the normal group has changed, it is simpler to refresh every UI
ui_refresh();
} else
set_hl_attr(idx);
HL_TABLE()[idx].sg_scriptID = current_SID;
@ -6678,10 +6582,11 @@ void restore_cterm_colors(void)
*/
static int hl_has_settings(int idx, int check_link)
{
return HL_TABLE()[idx].sg_term_attr != 0
|| HL_TABLE()[idx].sg_cterm_attr != 0
return HL_TABLE()[idx].sg_attr != 0
|| HL_TABLE()[idx].sg_cterm_fg != 0
|| HL_TABLE()[idx].sg_cterm_bg != 0
|| HL_TABLE()[idx].sg_rgb_fg_name != NULL
|| HL_TABLE()[idx].sg_rgb_bg_name != NULL
|| (check_link && (HL_TABLE()[idx].sg_set & SG_LINK));
}
@ -6690,17 +6595,11 @@ static int hl_has_settings(int idx, int check_link)
*/
static void highlight_clear(int idx)
{
HL_TABLE()[idx].sg_term = 0;
free(HL_TABLE()[idx].sg_start);
HL_TABLE()[idx].sg_start = NULL;
free(HL_TABLE()[idx].sg_stop);
HL_TABLE()[idx].sg_stop = NULL;
HL_TABLE()[idx].sg_term_attr = 0;
HL_TABLE()[idx].sg_attr = 0;
HL_TABLE()[idx].sg_cterm = 0;
HL_TABLE()[idx].sg_cterm_bold = FALSE;
HL_TABLE()[idx].sg_cterm_fg = 0;
HL_TABLE()[idx].sg_cterm_bg = 0;
HL_TABLE()[idx].sg_cterm_attr = 0;
HL_TABLE()[idx].sg_gui = 0;
HL_TABLE()[idx].sg_rgb_fg = -1;
HL_TABLE()[idx].sg_rgb_bg = -1;
@ -6720,23 +6619,20 @@ static void highlight_clear(int idx)
* Note that this table is used by ALL buffers. This is required because the
* GUI can redraw at any time for any buffer.
*/
static garray_T term_attr_table = GA_EMPTY_INIT_VALUE;
static garray_T attr_table = GA_EMPTY_INIT_VALUE;
#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
static garray_T cterm_attr_table = GA_EMPTY_INIT_VALUE;
#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
#define ATTR_ENTRY(idx) ((attrentry_T *)attr_table.ga_data)[idx]
/*
* Return the attr number for a set of colors and font.
* Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
* Add a new entry to the term_attr_table, attr_table or gui_attr_table
* if the combination is new.
* Return 0 for error.
*/
static int get_attr_entry(garray_T *table, attrentry_T *aep)
static int get_attr_entry(attrentry_T *aep)
{
garray_T *table = &attr_table;
attrentry_T *taep;
static int recursive = FALSE;
@ -6751,31 +6647,14 @@ static int get_attr_entry(garray_T *table, attrentry_T *aep)
*/
for (int i = 0; i < table->ga_len; ++i) {
taep = &(((attrentry_T *)table->ga_data)[i]);
if ( aep->ae_attr == taep->ae_attr
&& (
(table == &term_attr_table
&& (aep->ae_u.term.start == NULL)
== (taep->ae_u.term.start == NULL)
&& (aep->ae_u.term.start == NULL
|| STRCMP(aep->ae_u.term.start,
taep->ae_u.term.start) == 0)
&& (aep->ae_u.term.stop == NULL)
== (taep->ae_u.term.stop == NULL)
&& (aep->ae_u.term.stop == NULL
|| STRCMP(aep->ae_u.term.stop,
taep->ae_u.term.stop) == 0))
|| (table == &cterm_attr_table
&& aep->ae_u.cterm.fg_color
== taep->ae_u.cterm.fg_color
&& aep->ae_u.cterm.bg_color
== taep->ae_u.cterm.bg_color
&& aep->fg_color
== taep->fg_color
&& aep->bg_color
== taep->bg_color)
))
if (aep->cterm_ae_attr == taep->cterm_ae_attr
&& aep->cterm_fg_color == taep->cterm_fg_color
&& aep->cterm_bg_color == taep->cterm_bg_color
&& aep->rgb_ae_attr == taep->rgb_ae_attr
&& aep->rgb_fg_color == taep->rgb_fg_color
&& aep->rgb_bg_color == taep->rgb_bg_color) {
return i + ATTR_OFF;
}
}
if (table->ga_len + ATTR_OFF > MAX_TYPENR) {
@ -6801,156 +6680,83 @@ static int get_attr_entry(garray_T *table, attrentry_T *aep)
recursive = FALSE;
}
/*
* This is a new combination of colors and font, add an entry.
*/
// This is a new combination of colors and font, add an entry.
taep = GA_APPEND_VIA_PTR(attrentry_T, table);
memset(taep, 0, sizeof(*taep));
taep->ae_attr = aep->ae_attr;
if (table == &term_attr_table) {
if (aep->ae_u.term.start == NULL)
taep->ae_u.term.start = NULL;
else
taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
if (aep->ae_u.term.stop == NULL)
taep->ae_u.term.stop = NULL;
else
taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
} else if (table == &cterm_attr_table) {
taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
taep->fg_color = aep->fg_color;
taep->bg_color = aep->bg_color;
}
taep->cterm_ae_attr = aep->cterm_ae_attr;
taep->cterm_fg_color = aep->cterm_fg_color;
taep->cterm_bg_color = aep->cterm_bg_color;
taep->rgb_ae_attr = aep->rgb_ae_attr;
taep->rgb_fg_color = aep->rgb_fg_color;
taep->rgb_bg_color = aep->rgb_bg_color;
return table->ga_len - 1 + ATTR_OFF;
}
/*
* Clear all highlight tables.
*/
// Clear all highlight tables.
void clear_hl_tables(void)
{
attrentry_T *taep;
for (int i = 0; i < term_attr_table.ga_len; ++i) {
taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
free(taep->ae_u.term.start);
free(taep->ae_u.term.stop);
}
ga_clear(&term_attr_table);
ga_clear(&cterm_attr_table);
ga_clear(&attr_table);
}
/*
* Combine special attributes (e.g., for spelling) with other attributes
* (e.g., for syntax highlighting).
* "prim_attr" overrules "char_attr".
* This creates a new group when required.
* Since we expect there to be few spelling mistakes we don't cache the
* result.
* Return the resulting attributes.
*/
// Combine special attributes (e.g., for spelling) with other attributes
// (e.g., for syntax highlighting).
// "prim_attr" overrules "char_attr".
// This creates a new group when required.
// Since we expect there to be few spelling mistakes we don't cache the
// result.
// Return the resulting attributes.
int hl_combine_attr(int char_attr, int prim_attr)
{
attrentry_T *char_aep = NULL;
attrentry_T *spell_aep;
attrentry_T new_en;
if (char_attr == 0)
if (char_attr == 0) {
return prim_attr;
if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
return char_attr | prim_attr;
if (abstract_ui || t_colors > 1) {
if (char_attr > HL_ALL)
char_aep = syn_cterm_attr2entry(char_attr);
if (char_aep != NULL)
new_en = *char_aep;
else {
memset(&new_en, 0, sizeof(new_en));
if (char_attr <= HL_ALL)
new_en.ae_attr = char_attr;
}
if (prim_attr <= HL_ALL)
new_en.ae_attr |= prim_attr;
else {
spell_aep = syn_cterm_attr2entry(prim_attr);
if (spell_aep != NULL) {
new_en.ae_attr |= spell_aep->ae_attr;
if (spell_aep->ae_u.cterm.fg_color > 0)
new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
if (spell_aep->ae_u.cterm.bg_color > 0)
new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
if (spell_aep->fg_color >= 0)
new_en.fg_color = spell_aep->fg_color;
if (spell_aep->bg_color >= 0)
new_en.bg_color = spell_aep->bg_color;
}
}
return get_attr_entry(&cterm_attr_table, &new_en);
}
if (char_attr > HL_ALL)
char_aep = syn_term_attr2entry(char_attr);
if (char_aep != NULL)
// Find the entry for char_attr
char_aep = syn_cterm_attr2entry(char_attr);
if (char_aep != NULL) {
// Copy all attributes from char_aep to the new entry
new_en = *char_aep;
else {
} else {
memset(&new_en, 0, sizeof(new_en));
if (char_attr <= HL_ALL)
new_en.ae_attr = char_attr;
}
if (prim_attr <= HL_ALL)
new_en.ae_attr |= prim_attr;
else {
spell_aep = syn_term_attr2entry(prim_attr);
if (spell_aep != NULL) {
new_en.ae_attr |= spell_aep->ae_attr;
if (spell_aep->ae_u.term.start != NULL) {
new_en.ae_u.term.start = spell_aep->ae_u.term.start;
new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
}
spell_aep = syn_cterm_attr2entry(prim_attr);
if (spell_aep != NULL) {
new_en.cterm_ae_attr |= spell_aep->cterm_ae_attr;
new_en.rgb_ae_attr |= spell_aep->rgb_ae_attr;
if (spell_aep->cterm_fg_color > 0) {
new_en.cterm_fg_color = spell_aep->cterm_fg_color;
}
if (spell_aep->cterm_bg_color > 0) {
new_en.cterm_bg_color = spell_aep->cterm_bg_color;
}
if (spell_aep->rgb_fg_color >= 0) {
new_en.rgb_fg_color = spell_aep->rgb_fg_color;
}
if (spell_aep->rgb_bg_color >= 0) {
new_en.rgb_bg_color = spell_aep->rgb_bg_color;
}
}
return get_attr_entry(&term_attr_table, &new_en);
}
/*
* Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
* Only to be used when "attr" > HL_ALL.
*/
int syn_attr2attr(int attr)
{
attrentry_T *aep;
if (abstract_ui || t_colors > 1)
aep = syn_cterm_attr2entry(attr);
else
aep = syn_term_attr2entry(attr);
if (aep == NULL) /* highlighting not set */
return 0;
return aep->ae_attr;
}
attrentry_T *syn_term_attr2entry(int attr)
{
attr -= ATTR_OFF;
if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */
return NULL;
return &(TERM_ATTR_ENTRY(attr));
return get_attr_entry(&new_en);
}
attrentry_T *syn_cterm_attr2entry(int attr)
{
attr -= ATTR_OFF;
if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */
if (attr >= attr_table.ga_len) /* did ":syntax clear" */
return NULL;
return &(CTERM_ATTR_ENTRY(attr));
return &(ATTR_ENTRY(attr));
}
#define LIST_ATTR 1
@ -6964,13 +6770,6 @@ static void highlight_list_one(int id)
sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */
didh = highlight_list_arg(id, didh, LIST_ATTR,
sgp->sg_term, NULL, "term");
didh = highlight_list_arg(id, didh, LIST_STRING,
0, sgp->sg_start, "start");
didh = highlight_list_arg(id, didh, LIST_STRING,
0, sgp->sg_stop, "stop");
didh = highlight_list_arg(id, didh, LIST_ATTR,
sgp->sg_cterm, NULL, "cterm");
didh = highlight_list_arg(id, didh, LIST_INT,
@ -7049,7 +6848,7 @@ char_u *
highlight_has_attr (
int id,
int flag,
int modec /* 'g' for GUI, 'c' for cterm, 't' for term */
int modec // 'g' for GUI, 'c' for cterm
)
{
int attr;
@ -7057,12 +6856,11 @@ highlight_has_attr (
if (id <= 0 || id > highlight_ga.ga_len)
return NULL;
if (modec == 'g')
if (modec == 'g') {
attr = HL_TABLE()[id - 1].sg_gui;
else if (modec == 'c')
} else {
attr = HL_TABLE()[id - 1].sg_cterm;
else
attr = HL_TABLE()[id - 1].sg_term;
}
if (attr & flag)
return (char_u *)"1";
@ -7179,37 +6977,16 @@ set_hl_attr (
if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
return;
/*
* For the term mode: If there are other than "normal" highlighting
* attributes, need to allocate an attr number.
*/
if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
sgp->sg_term_attr = sgp->sg_term;
else {
at_en.ae_attr = sgp->sg_term;
at_en.ae_u.term.start = sgp->sg_start;
at_en.ae_u.term.stop = sgp->sg_stop;
sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
}
/*
* For the color term mode: If there are other than "normal"
* highlighting attributes, need to allocate an attr number.
*/
if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0
&& sgp->sg_rgb_fg == -1 && sgp->sg_rgb_bg == -1) {
sgp->sg_cterm_attr = sgp->sg_cterm;
} else {
at_en.ae_attr = abstract_ui ? sgp->sg_gui : sgp->sg_cterm;
at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
// FIXME(tarruda): The "unset value" for rgb is -1, but since hlgroup is
// initialized with 0(by garray functions), check for sg_rgb_{f,b}g_name
// before setting attr_entry->{f,g}g_color to a other than -1
at_en.fg_color = sgp->sg_rgb_fg_name ? sgp->sg_rgb_fg : -1;
at_en.bg_color = sgp->sg_rgb_bg_name ? sgp->sg_rgb_bg : -1;
sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
}
at_en.cterm_ae_attr = sgp->sg_cterm;
at_en.cterm_fg_color = sgp->sg_cterm_fg;
at_en.cterm_bg_color = sgp->sg_cterm_bg;
at_en.rgb_ae_attr = sgp->sg_gui;
// FIXME(tarruda): The "unset value" for rgb is -1, but since hlgroup is
// initialized with 0(by garray functions), check for sg_rgb_{f,b}g_name
// before setting attr_entry->{f,g}g_color to a other than -1
at_en.rgb_fg_color = sgp->sg_rgb_fg_name ? sgp->sg_rgb_fg : -1;
at_en.rgb_bg_color = sgp->sg_rgb_bg_name ? sgp->sg_rgb_bg : -1;
sgp->sg_attr = get_attr_entry(&at_en);
}
/*
@ -7348,18 +7125,11 @@ static void syn_unadd_group(void)
*/
int syn_id2attr(int hl_id)
{
int attr;
struct hl_group *sgp;
hl_id = syn_get_final_id(hl_id);
sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
if (abstract_ui || t_colors > 1)
attr = sgp->sg_cterm_attr;
else
attr = sgp->sg_term_attr;
return attr;
return sgp->sg_attr;
}
@ -7446,11 +7216,12 @@ int highlight_changed(void)
* bold-underlined.
*/
attr = 0;
bool colon = false;
for (; *p && *p != ','; ++p) { /* parse upto comma */
if (vim_iswhite(*p)) /* ignore white space */
continue;
if (attr > HL_ALL) /* Combination with ':' is not allowed. */
if (colon) /* Combination with ':' is not allowed. */
return FAIL;
switch (*p) {
@ -7472,6 +7243,7 @@ int highlight_changed(void)
case ':': ++p; /* highlight group name */
if (attr || *p == NUL) /* no combinations */
return FAIL;
colon = true;
end = vim_strchr(p, ',');
if (end == NULL)
end = p + STRLEN(p);
@ -7506,7 +7278,6 @@ int highlight_changed(void)
hlcnt = highlight_ga.ga_len;
if (id_S == 0) { /* Make sure id_S is always valid to simplify code below */
memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group));
HL_TABLE()[hlcnt + 9].sg_term = highlight_attr[HLF_S];
id_S = hlcnt + 10;
}
for (int i = 0; i < 9; i++) {
@ -7521,7 +7292,6 @@ int highlight_changed(void)
highlight_user[i] = syn_id2attr(id);
if (id_SNC == 0) {
memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
hlt[hlcnt + i].sg_term = highlight_attr[HLF_SNC];
hlt[hlcnt + i].sg_cterm = highlight_attr[HLF_SNC];
hlt[hlcnt + i].sg_gui = highlight_attr[HLF_SNC];
} else
@ -7531,20 +7301,26 @@ int highlight_changed(void)
hlt[hlcnt + i].sg_link = 0;
/* Apply difference between UserX and HLF_S to HLF_SNC */
hlt[hlcnt + i].sg_term ^=
hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
hlt[hlcnt + i].sg_cterm ^=
hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
hlt[hlcnt + i].sg_cterm ^= hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg) {
hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
}
if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg) {
hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
hlt[hlcnt + i].sg_gui ^=
hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
}
hlt[hlcnt + i].sg_gui ^= hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
if (hlt[id - 1].sg_rgb_fg != hlt[id_S - 1].sg_rgb_fg) {
hlt[hlcnt + i].sg_rgb_fg = hlt[id - 1].sg_rgb_fg;
}
if (hlt[id - 1].sg_rgb_bg != hlt[id_S - 1].sg_rgb_bg) {
hlt[hlcnt + i].sg_rgb_bg = hlt[id - 1].sg_rgb_bg;
}
highlight_ga.ga_len = hlcnt + i + 1;
set_hl_attr(hlcnt + i); /* At long last we can apply */
highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1);

View File

@ -16,7 +16,6 @@
#define HL_UNDERLINE 0x08
#define HL_UNDERCURL 0x10
#define HL_STANDOUT 0x20
#define HL_ALL 0x3f
#define HL_CONTAINED 0x01 /* not used on toplevel */
#define HL_TRANSP 0x02 /* has no highlighting */

View File

@ -67,23 +67,11 @@ struct syn_state {
* may have made the state invalid */
};
/*
* Structure shared between syntax.c, screen.c and gui_x11.c.
*/
// Structure shared between syntax.c, screen.c
typedef struct attr_entry {
short ae_attr; /* HL_BOLD, etc. */
RgbValue fg_color, bg_color;
union {
struct {
char_u *start; /* start escape sequence */
char_u *stop; /* stop escape sequence */
} term;
struct {
/* These colors need to be > 8 bits to hold 256. */
uint16_t fg_color; /* foreground color number */
uint16_t bg_color; /* background color number */
} cterm;
} ae_u;
short rgb_ae_attr, cterm_ae_attr; // HL_BOLD, etc.
RgbValue rgb_fg_color, rgb_bg_color;
int cterm_fg_color, cterm_bg_color;
} attrentry_T;
#endif // NVIM_SYNTAX_DEFS_H

File diff suppressed because it is too large Load Diff

246
src/nvim/tui/term_input.inl Normal file
View File

@ -0,0 +1,246 @@
#include <termkey.h>
#include "nvim/ascii.h"
#include "nvim/os/os.h"
#include "nvim/os/input.h"
#include "nvim/os/rstream.h"
struct term_input {
int in_fd;
TermKey *tk;
uv_tty_t input_handle;
uv_timer_t timer_handle;
RBuffer *read_buffer;
RStream *read_stream;
};
static void forward_simple_utf8(TermKeyKey *key)
{
size_t len = 0;
char buf[64];
char *ptr = key->utf8;
while (*ptr) {
if (*ptr == '<') {
len += (size_t)snprintf(buf + len, sizeof(buf) - len, "<lt>");
} else {
buf[len++] = *ptr;
}
ptr++;
}
buf[len] = 0;
input_enqueue((String){.data = buf, .size = len});
}
static void forward_modified_utf8(TermKey *tk, TermKeyKey *key)
{
size_t len;
char buf[64];
if (key->type == TERMKEY_TYPE_KEYSYM
&& key->code.sym == TERMKEY_SYM_ESCAPE) {
len = (size_t)snprintf(buf, sizeof(buf), "<Esc>");
} else {
len = termkey_strfkey(tk, buf, sizeof(buf), key, TERMKEY_FORMAT_VIM);
}
input_enqueue((String){.data = buf, .size = len});
}
static void forward_mouse_event(TermKey *tk, TermKeyKey *key)
{
char buf[64];
size_t len = 0;
int button, row, col;
TermKeyMouseEvent ev;
termkey_interpret_mouse(tk, key, &ev, &button, &row, &col);
if (ev != TERMKEY_MOUSE_PRESS && ev != TERMKEY_MOUSE_DRAG) {
return;
}
row--; col--; // Termkey uses 1-based coordinates
buf[len++] = '<';
if (key->modifiers & TERMKEY_KEYMOD_SHIFT) {
len += (size_t)snprintf(buf + len, sizeof(buf) - len, "S-");
}
if (key->modifiers & TERMKEY_KEYMOD_CTRL) {
len += (size_t)snprintf(buf + len, sizeof(buf) - len, "C-");
}
if (key->modifiers & TERMKEY_KEYMOD_ALT) {
len += (size_t)snprintf(buf + len, sizeof(buf) - len, "A-");
}
if (button == 1) {
len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Left");
} else if (button == 2) {
len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Middle");
} else if (button == 3) {
len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Right");
}
if (ev == TERMKEY_MOUSE_PRESS) {
if (button == 4) {
len += (size_t)snprintf(buf + len, sizeof(buf) - len, "ScrollWheelUp");
} else if (button == 5) {
len += (size_t)snprintf(buf + len, sizeof(buf) - len, "ScrollWheelDown");
} else {
len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Mouse");
}
} else if (ev == TERMKEY_MOUSE_DRAG) {
len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Drag");
}
len += (size_t)snprintf(buf + len, sizeof(buf) - len, "><%d,%d>", col, row);
input_enqueue((String){.data = buf, .size = len});
}
static TermKeyResult tk_getkey(TermKey *tk, TermKeyKey *key, bool force)
{
return force ? termkey_getkey_force(tk, key) : termkey_getkey(tk, key);
}
static void timer_cb(uv_timer_t *handle);
static int get_key_code_timeout(void)
{
Integer ms = 0;
bool timeout = false;
// Check 'timeout' and 'ttimeout' to determine if we should send ESC
// after 'ttimeoutlen'. See :help 'ttimeout' for more information
Error err;
timeout = vim_get_option(cstr_as_string("timeout"), &err).data.boolean;
if (!timeout) {
timeout = vim_get_option(cstr_as_string("ttimeout"), &err).data.boolean;
}
if (timeout) {
ms = vim_get_option(cstr_as_string("ttimeoutlen"), &err).data.integer;
}
return (int)ms;
}
static void tk_getkeys(TermInput *input, bool force)
{
TermKeyKey key;
TermKeyResult result;
while ((result = tk_getkey(input->tk, &key, force)) == TERMKEY_RES_KEY) {
if (key.type == TERMKEY_TYPE_UNICODE && !key.modifiers) {
forward_simple_utf8(&key);
} else if (key.type == TERMKEY_TYPE_UNICODE ||
key.type == TERMKEY_TYPE_FUNCTION ||
key.type == TERMKEY_TYPE_KEYSYM) {
forward_modified_utf8(input->tk, &key);
} else if (key.type == TERMKEY_TYPE_MOUSE) {
forward_mouse_event(input->tk, &key);
}
}
if (result != TERMKEY_RES_AGAIN) {
return;
}
int ms = get_key_code_timeout();
if (ms > 0) {
// Stop the current timer if already running
uv_timer_stop(&input->timer_handle);
uv_timer_start(&input->timer_handle, timer_cb, (uint32_t)ms, 0);
} else {
tk_getkeys(input, true);
}
}
static void timer_cb(uv_timer_t *handle)
{
tk_getkeys(handle->data, true);
}
static void read_cb(RStream *rstream, void *rstream_data, bool eof)
{
if (eof) {
input_done();
return;
}
TermInput *input = rstream_data;
do {
char *ptr = rbuffer_read_ptr(input->read_buffer);
size_t len = rbuffer_pending(input->read_buffer);
if (len > 1 && ptr[0] == ESC && ptr[1] == NUL) {
// skip the ESC and NUL and push one <esc> to the input buffer
termkey_push_bytes(input->tk, ptr, 1);
rbuffer_consumed(input->read_buffer, 2);
tk_getkeys(input, true);
continue;
}
// Find the next 'esc' and push everything up to it(excluding)
size_t i;
for (i = ptr[0] == ESC ? 1 : 0; i < len; i++) {
if (ptr[i] == '\x1b') {
break;
}
}
size_t consumed = termkey_push_bytes(input->tk, ptr, i);
rbuffer_consumed(input->read_buffer, consumed);
tk_getkeys(input, false);
} while (rbuffer_pending(input->read_buffer));
}
static TermInput *term_input_new(void)
{
TermInput *rv = xmalloc(sizeof(TermInput));
// read input from stderr if stdin is not a tty
rv->in_fd = os_isatty(0) ? 0 : (os_isatty(2) ? 2 : 0);
// Set terminal encoding based on environment(taken from libtermkey source
// code)
const char *e;
int flags = 0;
if (((e = os_getenv("LANG")) || (e = os_getenv("LC_MESSAGES"))
|| (e = os_getenv("LC_ALL"))) && (e = strchr(e, '.')) && e++ &&
(strcasecmp(e, "UTF-8") == 0 || strcasecmp(e, "UTF8") == 0)) {
flags |= TERMKEY_FLAG_UTF8;
} else {
flags |= TERMKEY_FLAG_RAW;
}
rv->tk = termkey_new_abstract(os_getenv("TERM"), flags);
int curflags = termkey_get_canonflags(rv->tk);
termkey_set_canonflags(rv->tk, curflags | TERMKEY_CANON_DELBS);
// setup input handle
uv_tty_init(uv_default_loop(), &rv->input_handle, rv->in_fd, 1);
uv_tty_set_mode(&rv->input_handle, UV_TTY_MODE_RAW);
rv->input_handle.data = NULL;
rv->read_buffer = rbuffer_new(0xfff);
rv->read_stream = rstream_new(read_cb, rv->read_buffer, rv);
rstream_set_stream(rv->read_stream, (uv_stream_t *)&rv->input_handle);
rstream_start(rv->read_stream);
// initialize a timer handle for handling ESC with libtermkey
uv_timer_init(uv_default_loop(), &rv->timer_handle);
rv->timer_handle.data = rv;
return rv;
}
static void term_input_destroy(TermInput *input)
{
uv_tty_reset_mode();
uv_timer_stop(&input->timer_handle);
rstream_stop(input->read_stream);
rstream_free(input->read_stream);
uv_close((uv_handle_t *)&input->input_handle, NULL);
uv_close((uv_handle_t *)&input->timer_handle, NULL);
termkey_destroy(input->tk);
event_poll(0); // Run once to remove references to input/timer handles
free(input->input_handle.data);
free(input);
}

752
src/nvim/tui/tui.c Normal file
View File

@ -0,0 +1,752 @@
#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <uv.h>
#include <unibilium.h>
#include "nvim/lib/kvec.h"
#include "nvim/vim.h"
#include "nvim/ui.h"
#include "nvim/map.h"
#include "nvim/memory.h"
#include "nvim/api/vim.h"
#include "nvim/api/private/helpers.h"
#include "nvim/os/event.h"
#include "nvim/tui/tui.h"
typedef struct term_input TermInput;
#include "term_input.inl"
typedef struct {
int top, bot, left, right;
} Rect;
typedef struct {
char data[7];
HlAttrs attrs;
} Cell;
typedef struct {
PMap(cstr_t) *option_cache;
unibi_var_t params[9];
char buf[0xffff];
size_t bufpos;
TermInput *input;
uv_loop_t *write_loop;
unibi_term *ut;
uv_tty_t output_handle;
uv_signal_t winch_handle;
Rect scroll_region;
kvec_t(Rect) invalid_regions;
int row, col;
int bg, fg;
int out_fd;
int old_height;
bool can_use_terminal_scroll;
HlAttrs attrs, print_attrs;
Cell **screen;
struct {
size_t enable_mouse, disable_mouse;
} unibi_ext;
} TUIData;
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "tui/tui.c.generated.h"
#endif
#define EMPTY_ATTRS ((HlAttrs){false, false, false, false, false, -1, -1})
#define FOREACH_CELL(ui, top, bot, left, right, go, code) \
do { \
TUIData *data = ui->data; \
for (int row = top; row <= bot; ++row) { \
Cell *cells = data->screen[row]; \
if (go) { \
unibi_goto(ui, row, left); \
} \
for (int col = left; col <= right; ++col) { \
Cell *cell = cells + col; \
(void)(cell); \
code; \
} \
} \
} while (0)
void tui_start(void)
{
TUIData *data = xcalloc(1, sizeof(TUIData));
UI *ui = xcalloc(1, sizeof(UI));
ui->data = data;
data->attrs = data->print_attrs = EMPTY_ATTRS;
data->fg = data->bg = -1;
data->can_use_terminal_scroll = true;
data->bufpos = 0;
data->option_cache = pmap_new(cstr_t)();
// write output to stderr if stdout is not a tty
data->out_fd = os_isatty(1) ? 1 : (os_isatty(2) ? 2 : 1);
kv_init(data->invalid_regions);
// setup term input
data->input = term_input_new();
// setup unibilium
data->ut = unibi_from_env();
if (!data->ut) {
// For some reason could not read terminfo file, use a dummy entry that
// will be populated with common values by fix_terminfo below
data->ut = unibi_dummy();
}
fix_terminfo(data);
// Enter alternate screen and clear
unibi_out(ui, unibi_enter_ca_mode, NULL);
unibi_out(ui, unibi_clear_screen, NULL);
// setup output handle in a separate event loop(we wanna do synchronous
// write to the tty)
data->write_loop = xmalloc(sizeof(uv_loop_t));
uv_loop_init(data->write_loop);
uv_tty_init(data->write_loop, &data->output_handle, data->out_fd, 0);
// Obtain screen dimensions
update_size(ui);
// listen for SIGWINCH
uv_signal_init(uv_default_loop(), &data->winch_handle);
uv_signal_start(&data->winch_handle, sigwinch_cb, SIGWINCH);
data->winch_handle.data = ui;
ui->stop = tui_stop;
ui->rgb = false;
ui->data = data;
ui->resize = tui_resize;
ui->clear = tui_clear;
ui->eol_clear = tui_eol_clear;
ui->cursor_goto = tui_cursor_goto;
ui->cursor_on = tui_cursor_on;
ui->cursor_off = tui_cursor_off;
ui->mouse_on = tui_mouse_on;
ui->mouse_off = tui_mouse_off;
ui->insert_mode = tui_insert_mode;
ui->normal_mode = tui_normal_mode;
ui->set_scroll_region = tui_set_scroll_region;
ui->scroll = tui_scroll;
ui->highlight_set = tui_highlight_set;
ui->put = tui_put;
ui->bell = tui_bell;
ui->visual_bell = tui_visual_bell;
ui->update_fg = tui_update_fg;
ui->update_bg = tui_update_bg;
ui->flush = tui_flush;
ui->suspend = tui_suspend;
ui->set_title = tui_set_title;
ui->set_icon = tui_set_icon;
// Attach
ui_attach(ui);
}
static void tui_stop(UI *ui)
{
TUIData *data = ui->data;
// Destroy common stuff
kv_destroy(data->invalid_regions);
uv_signal_stop(&data->winch_handle);
uv_close((uv_handle_t *)&data->winch_handle, NULL);
// Destroy input stuff
term_input_destroy(data->input);
// Destroy output stuff
tui_normal_mode(ui);
tui_mouse_off(ui);
unibi_out(ui, unibi_exit_attribute_mode, NULL);
unibi_out(ui, unibi_cursor_normal, NULL);
unibi_out(ui, unibi_exit_ca_mode, NULL);
flush_buf(ui);
uv_close((uv_handle_t *)&data->output_handle, NULL);
uv_run(data->write_loop, UV_RUN_DEFAULT);
if (uv_loop_close(data->write_loop)) {
abort();
}
free(data->write_loop);
unibi_destroy(data->ut);
char *opt_value;
map_foreach_value(data->option_cache, opt_value, {
free(opt_value);
});
pmap_free(cstr_t)(data->option_cache);
destroy_screen(data);
free(data);
free(ui);
ui_detach(ui);
}
static void try_resize(Event ev)
{
UI *ui = ev.data;
update_size(ui);
ui_refresh();
}
static void sigwinch_cb(uv_signal_t *handle, int signum)
{
// Queue the event because resizing can result in recursive event_poll calls
event_push((Event) {
.data = handle->data,
.handler = try_resize
}, false);
}
static bool attrs_differ(HlAttrs a1, HlAttrs a2)
{
return a1.foreground != a2.foreground || a1.background != a2.background
|| a1.bold != a2.bold || a1.italic != a2.italic
|| a1.undercurl != a2.undercurl || a1.underline != a2.underline
|| a1.reverse != a2.reverse;
}
static void update_attrs(UI *ui, HlAttrs attrs)
{
TUIData *data = ui->data;
unibi_out(ui, unibi_exit_attribute_mode, NULL);
data->params[0].i = attrs.foreground != -1 ? attrs.foreground : data->fg;
if (data->params[0].i != -1) {
unibi_out(ui, unibi_set_a_foreground, NULL);
}
data->params[0].i = attrs.background != -1 ? attrs.background : data->bg;
if (data->params[0].i != -1) {
unibi_out(ui, unibi_set_a_background, NULL);
}
if (attrs.bold) {
unibi_out(ui, unibi_enter_bold_mode, NULL);
}
if (attrs.italic) {
unibi_out(ui, unibi_enter_italics_mode, NULL);
}
if (attrs.underline) {
unibi_out(ui, unibi_enter_underline_mode, NULL);
}
if (attrs.reverse) {
unibi_out(ui, unibi_enter_reverse_mode, NULL);
}
}
static void print_cell(UI *ui, Cell *ptr)
{
TUIData *data = ui->data;
if (attrs_differ(ptr->attrs, data->print_attrs)) {
update_attrs(ui, ptr->attrs);
data->print_attrs = ptr->attrs;
}
out(ui, ptr->data);
}
static void clear_region(UI *ui, int top, int bot, int left, int right,
bool refresh)
{
TUIData *data = ui->data;
HlAttrs clear_attrs = EMPTY_ATTRS;
clear_attrs.foreground = data->fg;
clear_attrs.background = data->bg;
bool cleared = false;
if (refresh && data->bg == -1 && right == ui->width -1) {
// Background is set to the default color and the right edge matches the
// screen end, try to use terminal codes for clearing the requested area.
if (left == 0) {
if (bot == ui->height - 1) {
if (top == 0) {
unibi_out(ui, unibi_clear_screen, NULL);
} else {
unibi_goto(ui, top, 0);
unibi_out(ui, unibi_clr_eos, NULL);
}
cleared = true;
}
}
if (!cleared) {
// iterate through each line and clear with clr_eol
for (int row = top; row <= bot; ++row) {
unibi_goto(ui, row, left);
unibi_out(ui, unibi_clr_eol, NULL);
}
cleared = true;
}
}
bool clear = refresh && !cleared;
FOREACH_CELL(ui, top, bot, left, right, clear, {
cell->data[0] = ' ';
cell->data[1] = 0;
cell->attrs = clear_attrs;
if (clear) {
print_cell(ui, cell);
}
});
// restore cursor
unibi_goto(ui, data->row, data->col);
}
static void tui_resize(UI *ui, int width, int height)
{
TUIData *data = ui->data;
destroy_screen(data);
data->screen = xmalloc((size_t)height * sizeof(Cell *));
for (int i = 0; i < height; i++) {
data->screen[i] = xcalloc((size_t)width, sizeof(Cell));
}
data->old_height = height;
data->scroll_region.top = 0;
data->scroll_region.bot = height - 1;
data->scroll_region.left = 0;
data->scroll_region.right = width - 1;
data->row = data->col = 0;
}
static void tui_clear(UI *ui)
{
TUIData *data = ui->data;
clear_region(ui, data->scroll_region.top, data->scroll_region.bot,
data->scroll_region.left, data->scroll_region.right, true);
}
static void tui_eol_clear(UI *ui)
{
TUIData *data = ui->data;
clear_region(ui, data->row, data->row, data->col,
data->scroll_region.right, true);
}
static void tui_cursor_goto(UI *ui, int row, int col)
{
TUIData *data = ui->data;
data->row = row;
data->col = col;
unibi_goto(ui, row, col);
}
static void tui_cursor_on(UI *ui)
{
unibi_out(ui, unibi_cursor_normal, NULL);
}
static void tui_cursor_off(UI *ui)
{
unibi_out(ui, unibi_cursor_invisible, NULL);
}
static void tui_mouse_on(UI *ui)
{
TUIData *data = ui->data;
unibi_out(ui, (int)data->unibi_ext.enable_mouse, NULL);
}
static void tui_mouse_off(UI *ui)
{
TUIData *data = ui->data;
unibi_out(ui, (int)data->unibi_ext.disable_mouse, NULL);
}
static void tui_insert_mode(UI *ui)
{
unibi_out(ui, -1, "t_SI");
}
static void tui_normal_mode(UI *ui)
{
unibi_out(ui, -1, "t_EI");
}
static void tui_set_scroll_region(UI *ui, int top, int bot, int left,
int right)
{
TUIData *data = ui->data;
data->scroll_region.top = top;
data->scroll_region.bot = bot;
data->scroll_region.left = left;
data->scroll_region.right = right;
data->can_use_terminal_scroll =
left == 0 && right == ui->width - 1
&& ((top == 0 && bot == ui->height - 1)
|| unibi_get_str(data->ut, unibi_change_scroll_region));
}
static void tui_scroll(UI *ui, int count)
{
TUIData *data = ui->data;
int top = data->scroll_region.top;
int bot = data->scroll_region.bot;
int left = data->scroll_region.left;
int right = data->scroll_region.right;
if (data->can_use_terminal_scroll) {
// Change terminal scroll region and move cursor to the top
data->params[0].i = top;
data->params[1].i = bot;
unibi_out(ui, unibi_change_scroll_region, NULL);
unibi_goto(ui, top, left);
}
// Compute start/stop/step for the loop below, also use terminal scroll
// if possible
int start, stop, step;
if (count > 0) {
start = top;
stop = bot - count + 1;
step = 1;
if (data->can_use_terminal_scroll) {
if (count == 1) {
unibi_out(ui, unibi_delete_line, NULL);
} else {
data->params[0].i = count;
unibi_out(ui, unibi_parm_delete_line, NULL);
}
}
} else {
start = bot;
stop = top - count - 1;
step = -1;
if (data->can_use_terminal_scroll) {
if (count == -1) {
unibi_out(ui, unibi_insert_line, NULL);
} else {
data->params[0].i = -count;
unibi_out(ui, unibi_parm_insert_line, NULL);
}
}
}
if (data->can_use_terminal_scroll) {
// Restore terminal scroll region and cursor
data->params[0].i = 0;
data->params[1].i = ui->height - 1;
unibi_out(ui, unibi_change_scroll_region, NULL);
unibi_goto(ui, data->row, data->col);
}
int i;
// Scroll internal screen
for (i = start; i != stop; i += step) {
Cell *target_row = data->screen[i] + left;
Cell *source_row = data->screen[i + count] + left;
memcpy(target_row, source_row, sizeof(Cell) * (size_t)(right - left + 1));
}
// clear emptied region, updating the terminal if its builtin scrolling
// facility was used. This is done when the background color is not the
// default, since scrolling may leave wrong background in the cleared area.
bool update_clear = data->bg != -1 && data->can_use_terminal_scroll;
if (count > 0) {
clear_region(ui, stop, stop + count - 1, left, right, update_clear);
} else {
clear_region(ui, stop + count + 1, stop, left, right, update_clear);
}
if (!data->can_use_terminal_scroll) {
// Mark the entire scroll region as invalid for redrawing later
invalidate(ui, data->scroll_region.top, data->scroll_region.bot,
data->scroll_region.left, data->scroll_region.right);
}
}
static void tui_highlight_set(UI *ui, HlAttrs attrs)
{
((TUIData *)ui->data)->attrs = attrs;
}
static void tui_put(UI *ui, uint8_t *text, size_t size)
{
TUIData *data = ui->data;
Cell *cell = data->screen[data->row] + data->col;
cell->data[size] = 0;
cell->attrs = data->attrs;
if (text) {
memcpy(cell->data, text, size);
}
print_cell(ui, cell);
data->col += 1;
}
static void tui_bell(UI *ui)
{
unibi_out(ui, unibi_bell, NULL);
}
static void tui_visual_bell(UI *ui)
{
unibi_out(ui, unibi_flash_screen, NULL);
}
static void tui_update_fg(UI *ui, int fg)
{
((TUIData *)ui->data)->fg = fg;
}
static void tui_update_bg(UI *ui, int bg)
{
((TUIData *)ui->data)->bg = bg;
}
static void tui_flush(UI *ui)
{
TUIData *data = ui->data;
while (kv_size(data->invalid_regions)) {
Rect r = kv_pop(data->invalid_regions);
FOREACH_CELL(ui, r.top, r.bot, r.left, r.right, true, {
print_cell(ui, cell);
});
}
unibi_goto(ui, data->row, data->col);
flush_buf(ui);
}
static void tui_suspend(UI *ui)
{
tui_stop(ui);
kill(0, SIGTSTP);
tui_start();
}
static void tui_set_title(UI *ui, char *title)
{
TUIData *data = ui->data;
if (!(unibi_get_str(data->ut, unibi_to_status_line)
&& unibi_get_str(data->ut, unibi_from_status_line))) {
return;
}
unibi_out(ui, unibi_to_status_line, NULL);
out(ui, title);
unibi_out(ui, unibi_from_status_line, NULL);
}
static void tui_set_icon(UI *ui, char *icon)
{
}
static void invalidate(UI *ui, int top, int bot, int left, int right)
{
TUIData *data = ui->data;
Rect *intersects = NULL;
// Increase dimensions before comparing to ensure adjacent regions are
// treated as intersecting
--top;
++bot;
--left;
++right;
for (size_t i = 0; i < kv_size(data->invalid_regions); i++) {
Rect *r = &kv_A(data->invalid_regions, i);
if (!(top > r->bot || bot < r->top
|| left > r->right || right < r->left)) {
intersects = r;
break;
}
}
++top;
--bot;
++left;
--right;
if (intersects) {
// If top/bot/left/right intersects with a invalid rect, we replace it
// by the union
intersects->top = MIN(top, intersects->top);
intersects->bot = MAX(bot, intersects->bot);
intersects->left = MIN(left, intersects->left);
intersects->right = MAX(right, intersects->right);
} else {
// Else just add a new entry;
kv_push(Rect, data->invalid_regions, ((Rect){top, bot, left, right}));
}
}
static void update_size(UI *ui)
{
TUIData *data = ui->data;
int width = 0, height = 0;
// 1 - try from a system call(ioctl/TIOCGWINSZ on unix)
if (!uv_tty_get_winsize(&data->output_handle, &width, &height)) {
goto end;
}
// 2 - use $LINES/$COLUMNS if available
const char *val;
int advance;
if ((val = os_getenv("LINES"))
&& sscanf(val, "%d%n", &height, &advance) != EOF && advance
&& (val = os_getenv("COLUMNS"))
&& sscanf(val, "%d%n", &width, &advance) != EOF && advance) {
goto end;
}
// 3- read from terminfo if available
height = unibi_get_num(data->ut, unibi_lines);
width = unibi_get_num(data->ut, unibi_columns);
end:
if (width <= 0 || height <= 0) {
// use a default of 80x24
width = 80;
height = 24;
}
ui->width = width;
ui->height = height;
}
static void unibi_goto(UI *ui, int row, int col)
{
TUIData *data = ui->data;
data->params[0].i = row;
data->params[1].i = col;
unibi_out(ui, unibi_cursor_address, NULL);
}
static void unibi_out(UI *ui, int unibi_index, char *nvim_override)
{
TUIData *data = ui->data;
const char *str = NULL;
if (nvim_override) {
str = get_term_option(ui, nvim_override);
} else if (unibi_index >= 0) {
if (unibi_index < unibi_string_begin_) {
str = unibi_get_ext_str(data->ut, (unsigned)unibi_index);
} else {
str = unibi_get_str(data->ut, (unsigned)unibi_index);
}
}
if (str) {
data->bufpos += unibi_run(str, data->params, data->buf + data->bufpos,
sizeof(data->buf) - data->bufpos);
}
}
static void out(UI *ui, const char *str)
{
TUIData *data = ui->data;
data->bufpos += (size_t)snprintf(data->buf + data->bufpos,
sizeof(data->buf) - data->bufpos, "%s", str);
}
static void unibi_set_if_empty(unibi_term *ut, enum unibi_string str,
const char *val)
{
if (!unibi_get_str(ut, str)) {
unibi_set_str(ut, str, val);
}
}
static void fix_terminfo(TUIData *data)
{
unibi_term *ut = data->ut;
const char *term = os_getenv("TERM");
if (!term) {
goto end;
}
#define STARTS_WITH(str, prefix) (!memcmp(str, prefix, sizeof(prefix) - 1))
if (STARTS_WITH(term, "rxvt")) {
unibi_set_if_empty(ut, unibi_exit_attribute_mode, "\x1b[m\x1b(B");
unibi_set_if_empty(ut, unibi_flash_screen, "\x1b[?5h$<20/>\x1b[?5l");
unibi_set_if_empty(ut, unibi_enter_italics_mode, "\x1b[3m");
} else if (STARTS_WITH(term, "screen")) {
unibi_set_if_empty(ut, unibi_to_status_line, "\x1b_");
unibi_set_if_empty(ut, unibi_from_status_line, "\x1b\\");
}
if (STARTS_WITH(term, "xterm") || STARTS_WITH(term, "rxvt")) {
unibi_set_if_empty(ut, unibi_cursor_normal, "\x1b[?12l\x1b[?25h");
unibi_set_if_empty(ut, unibi_cursor_invisible, "\x1b[?25l");
unibi_set_if_empty(ut, unibi_flash_screen, "\x1b[?5h$<100/>\x1b[?5l");
unibi_set_if_empty(ut, unibi_exit_attribute_mode, "\x1b(B\x1b[m");
unibi_set_if_empty(ut, unibi_change_scroll_region, "\x1b[%i%p1%d;%p2%dr");
unibi_set_if_empty(ut, unibi_clear_screen, "\x1b[H\x1b[2J");
unibi_set_if_empty(ut, unibi_to_status_line, "\x1b]2");
unibi_set_if_empty(ut, unibi_from_status_line, "\x07");
}
end:
// Fill some empty slots with common terminal strings
data->unibi_ext.enable_mouse = unibi_add_ext_str(ut, NULL,
"\x1b[?1002h\x1b[?1006h");
data->unibi_ext.disable_mouse = unibi_add_ext_str(ut, NULL,
"\x1b[?1002l\x1b[?1006l");
unibi_set_if_empty(ut, unibi_cursor_address, "\x1b[%i%p1%d;%p2%dH");
unibi_set_if_empty(ut, unibi_exit_attribute_mode, "\x1b[0;10m");
unibi_set_if_empty(ut, unibi_set_a_foreground,
"\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m");
unibi_set_if_empty(ut, unibi_set_a_background,
"\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m");
unibi_set_if_empty(ut, unibi_enter_bold_mode, "\x1b[1m");
unibi_set_if_empty(ut, unibi_enter_underline_mode, "\x1b[4m");
unibi_set_if_empty(ut, unibi_enter_reverse_mode, "\x1b[7m");
unibi_set_if_empty(ut, unibi_bell, "\x07");
unibi_set_if_empty(data->ut, unibi_enter_ca_mode, "\x1b[?1049h");
unibi_set_if_empty(data->ut, unibi_exit_ca_mode, "\x1b[?1049l");
unibi_set_if_empty(ut, unibi_delete_line, "\x1b[M");
unibi_set_if_empty(ut, unibi_parm_delete_line, "\x1b[%p1%dM");
unibi_set_if_empty(ut, unibi_insert_line, "\x1b[L");
unibi_set_if_empty(ut, unibi_parm_insert_line, "\x1b[%p1%dL");
unibi_set_if_empty(ut, unibi_clear_screen, "\x1b[H\x1b[J");
unibi_set_if_empty(ut, unibi_clr_eol, "\x1b[K");
unibi_set_if_empty(ut, unibi_clr_eos, "\x1b[J");
}
static void flush_buf(UI *ui)
{
static uv_write_t req;
static uv_buf_t buf;
TUIData *data = ui->data;
buf.base = data->buf;
buf.len = data->bufpos;
uv_write(&req, (uv_stream_t *)&data->output_handle, &buf, 1, NULL);
uv_run(data->write_loop, UV_RUN_DEFAULT);
data->bufpos = 0;
}
static char *get_term_option(UI *ui, char *option)
{
TUIData *data = ui->data;
char *rv = pmap_get(cstr_t)(data->option_cache, option);
if (!rv) {
Error err;
Object val = vim_get_option(cstr_as_string(option), &err);
if (val.type == kObjectTypeString) {
rv = val.data.string.data;
pmap_put(cstr_t)(data->option_cache, option, rv);
}
}
return rv;
}
static void destroy_screen(TUIData *data)
{
if (data->screen) {
for (int i = 0; i < data->old_height; i++) {
free(data->screen[i]);
}
free(data->screen);
}
}

8
src/nvim/tui/tui.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef NVIM_TUI_TUI_H
#define NVIM_TUI_TUI_H
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "tui/tui.h.generated.h"
#endif
#endif // NVIM_TUI_TUI_H

View File

@ -46,6 +46,7 @@
#include "nvim/syntax.h"
#include "nvim/term.h"
#include "nvim/window.h"
#include "nvim/tui/tui.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ui.c.generated.h"
@ -59,8 +60,8 @@ static int row, col;
static struct {
int top, bot, left, right;
} sr;
static int current_highlight_mask = 0;
static bool cursor_enabled = true;
static int current_attr_code = 0;
static bool cursor_enabled = true, pending_cursor_update = false;
static int height, width;
// This set of macros allow us to use UI_CALL to invoke any function on
@ -71,6 +72,7 @@ static int height, width;
// works.
#define UI_CALL(...) \
do { \
flush_cursor_update(); \
for (size_t i = 0; i < ui_count; i++) { \
UI *ui = uis[i]; \
UI_CALL_HELPER(CNT(__VA_ARGS__), __VA_ARGS__); \
@ -80,8 +82,18 @@ static int height, width;
#define SELECT_NTH(a1, a2, a3, a4, a5, a6, ...) a6
#define UI_CALL_HELPER(c, ...) UI_CALL_HELPER2(c, __VA_ARGS__)
#define UI_CALL_HELPER2(c, ...) UI_CALL_##c(__VA_ARGS__)
#define UI_CALL_MORE(method, ...) ui->method(ui, __VA_ARGS__)
#define UI_CALL_ZERO(method) ui->method(ui)
#define UI_CALL_MORE(method, ...) if (ui->method) ui->method(ui, __VA_ARGS__)
#define UI_CALL_ZERO(method) if (ui->method) ui->method(ui)
void ui_builtin_start(void)
{
tui_start();
}
void ui_builtin_stop(void)
{
UI_CALL(stop);
}
void ui_write(uint8_t *s, int len)
{
@ -90,28 +102,7 @@ void ui_write(uint8_t *s, int len)
return;
}
if (abstract_ui) {
parse_abstract_ui_codes(s, len);
return;
}
if (!len) {
return;
}
char_u *tofree = NULL;
if (output_conv.vc_type != CONV_NONE) {
/* Convert characters from 'encoding' to 'termencoding'. */
tofree = string_convert(&output_conv, s, &len);
if (tofree != NULL)
s = tofree;
}
term_write(s, len);
if (output_conv.vc_type != CONV_NONE)
free(tofree);
parse_abstract_ui_codes(s, len);
}
bool ui_rgb_attached(void)
@ -124,6 +115,11 @@ bool ui_rgb_attached(void)
return false;
}
bool ui_active(void)
{
return ui_count != 0;
}
/*
* If the machine has job control, use it to suspend the program,
* otherwise fake it by starting a new shell.
@ -131,12 +127,8 @@ bool ui_rgb_attached(void)
*/
void ui_suspend(void)
{
if (abstract_ui) {
UI_CALL(suspend);
UI_CALL(flush);
} else {
mch_suspend();
}
UI_CALL(suspend);
UI_CALL(flush);
}
void ui_set_title(char *title)
@ -151,47 +143,17 @@ void ui_set_icon(char *icon)
UI_CALL(flush);
}
/*
* Try to get the current Vim shell size. Put the result in Rows and Columns.
* Use the new sizes as defaults for 'columns' and 'lines'.
* Return OK when size could be determined, FAIL otherwise.
*/
int ui_get_shellsize(void)
{
if (abstract_ui) {
return FAIL;
}
int retval;
retval = mch_get_shellsize();
check_shellsize();
/* adjust the default for 'lines' and 'columns' */
if (retval == OK) {
set_number_default("lines", Rows);
set_number_default("columns", Columns);
}
return retval;
}
/*
* May update the shape of the cursor.
*/
void ui_cursor_shape(void)
{
if (abstract_ui) {
ui_change_mode();
} else {
term_cursor_shape();
conceal_check_cursur_line();
}
ui_change_mode();
}
void ui_refresh(void)
{
if (!ui_count) {
if (!ui_active()) {
return;
}
@ -203,7 +165,7 @@ void ui_refresh(void)
height = ui->height < height ? ui->height : height;
}
screen_resize(width, height, true);
screen_resize(width, height);
}
void ui_resize(int new_width, int new_height)
@ -241,20 +203,12 @@ void ui_cursor_off(void)
void ui_mouse_on(void)
{
if (abstract_ui) {
UI_CALL(mouse_on);
} else {
mch_setmouse(true);
}
UI_CALL(mouse_on);
}
void ui_mouse_off(void)
{
if (abstract_ui) {
UI_CALL(mouse_off);
} else {
mch_setmouse(false);
}
UI_CALL(mouse_off);
}
// Notify that the current mode has changed. Can be used to change cursor
@ -319,77 +273,82 @@ void ui_detach(UI *ui)
}
}
static void highlight_start(int mask)
static void highlight_start(int attr_code)
{
if (mask > HL_ALL) {
// attribute code
current_highlight_mask = mask;
} else {
// attribute mask
current_highlight_mask |= mask;
}
current_attr_code = attr_code;
if (!ui_count) {
return;
}
set_highlight_args(current_highlight_mask);
set_highlight_args(current_attr_code);
}
static void highlight_stop(int mask)
{
if (mask > HL_ALL) {
// attribute code
current_highlight_mask = HL_NORMAL;
} else {
// attribute mask
current_highlight_mask &= ~mask;
current_attr_code = HL_NORMAL;
if (!ui_count) {
return;
}
set_highlight_args(current_highlight_mask);
set_highlight_args(current_attr_code);
}
static void set_highlight_args(int mask)
static void set_highlight_args(int attr_code)
{
HlAttrs rgb_attrs = { false, false, false, false, false, -1, -1 };
attrentry_T *aep = NULL;
if (mask > HL_ALL) {
aep = syn_cterm_attr2entry(mask);
mask = aep ? aep->ae_attr : 0;
}
rgb_attrs.bold = mask & HL_BOLD;
rgb_attrs.underline = mask & HL_UNDERLINE;
rgb_attrs.undercurl = mask & HL_UNDERCURL;
rgb_attrs.italic = mask & HL_ITALIC;
rgb_attrs.reverse = mask & (HL_INVERSE | HL_STANDOUT);
HlAttrs cterm_attrs = rgb_attrs;
if (aep) {
if (aep->fg_color != normal_fg) {
rgb_attrs.foreground = aep->fg_color;
}
if (aep->bg_color != normal_bg) {
rgb_attrs.background = aep->bg_color;
}
if (cterm_normal_fg_color != aep->ae_u.cterm.fg_color) {
cterm_attrs.foreground = aep->ae_u.cterm.fg_color - 1;
}
if (cterm_normal_bg_color != aep->ae_u.cterm.bg_color) {
cterm_attrs.background = aep->ae_u.cterm.bg_color - 1;
}
if (attr_code == HL_NORMAL) {
goto end;
}
int rgb_mask = 0;
int cterm_mask = 0;
attrentry_T *aep = syn_cterm_attr2entry(attr_code);
if (!aep) {
goto end;
}
rgb_mask = aep->rgb_ae_attr;
cterm_mask = aep->cterm_ae_attr;
rgb_attrs.bold = rgb_mask & HL_BOLD;
rgb_attrs.underline = rgb_mask & HL_UNDERLINE;
rgb_attrs.undercurl = rgb_mask & HL_UNDERCURL;
rgb_attrs.italic = rgb_mask & HL_ITALIC;
rgb_attrs.reverse = rgb_mask & (HL_INVERSE | HL_STANDOUT);
cterm_attrs.bold = cterm_mask & HL_BOLD;
cterm_attrs.underline = cterm_mask & HL_UNDERLINE;
cterm_attrs.undercurl = cterm_mask & HL_UNDERCURL;
cterm_attrs.italic = cterm_mask & HL_ITALIC;
cterm_attrs.reverse = cterm_mask & (HL_INVERSE | HL_STANDOUT);
if (aep->rgb_fg_color != normal_fg) {
rgb_attrs.foreground = aep->rgb_fg_color;
}
if (aep->rgb_bg_color != normal_bg) {
rgb_attrs.background = aep->rgb_bg_color;
}
if (cterm_normal_fg_color != aep->cterm_fg_color) {
cterm_attrs.foreground = aep->cterm_fg_color - 1;
}
if (cterm_normal_bg_color != aep->cterm_bg_color) {
cterm_attrs.background = aep->cterm_bg_color - 1;
}
end:
UI_CALL(highlight_set, (ui->rgb ? rgb_attrs : cterm_attrs));
}
static void parse_abstract_ui_codes(uint8_t *ptr, int len)
{
if (!ui_count) {
if (!ui_active()) {
return;
}
@ -558,5 +517,13 @@ static void ui_cursor_goto(int new_row, int new_col)
}
row = new_row;
col = new_col;
UI_CALL(cursor_goto, row, col);
pending_cursor_update = true;
}
static void flush_cursor_update(void)
{
if (pending_cursor_update) {
pending_cursor_update = false;
UI_CALL(cursor_goto, row, col);
}
}

View File

@ -38,6 +38,7 @@ struct ui_t {
void (*suspend)(UI *ui);
void (*set_title)(UI *ui, char *title);
void (*set_icon)(UI *ui, char *icon);
void (*stop)(UI *ui);
};
#ifdef INCLUDE_GENERATED_DECLARATIONS

View File

@ -136,21 +136,6 @@ static char *(features[]) = {
"+tag_binary",
"+tag_old_static",
"-tag_any_white",
#if defined(UNIX)
// only Unix can have terminfo instead of termcap
# ifdef TERMINFO
"+terminfo",
# else // ifdef TERMINFO
"-terminfo",
# endif // ifdef TERMINFO
#else // unix always includes termcap support
# ifdef HAVE_TGETENT
"+tgetent",
# else // ifdef HAVE_TGETENT
"-tgetent",
# endif // ifdef HAVE_TGETENT
#endif // if defined(UNIX)
"+termresponse",
"+textobjects",
"+title",
@ -813,19 +798,6 @@ static char *(extra_patches[]) = {
NULL
};
int highest_patch(void)
{
int i;
int h = 0;
for (i = 0; included_patches[i] != 0; ++i) {
if (included_patches[i] > h) {
h = included_patches[i];
}
}
return h;
}
/// Checks whether patch `n` has been included.
///
/// @param n The patch number.

View File

@ -8,7 +8,8 @@
#ifndef NVIM_VIM_H
# define NVIM_VIM_H
#define min(X, Y) (X < Y ? X : Y)
#define MIN(X, Y) (X < Y ? X : Y)
#define MAX(X, Y) (X > Y ? X : Y)
#include "nvim/types.h"
#include "nvim/pos.h" // for linenr_T, MAXCOL, etc...

View File

@ -4,6 +4,7 @@ local clear, nvim, buffer, curbuf, curbuf_contents, window, curwin, eq, neq,
ok, feed, rawfeed, insert, eval = helpers.clear, helpers.nvim, helpers.buffer, helpers.curbuf,
helpers.curbuf_contents, helpers.window, helpers.curwin, helpers.eq,
helpers.neq, helpers.ok, helpers.feed, helpers.rawfeed, helpers.insert, helpers.eval
local wait = helpers.wait
-- check if str is visible at the beginning of some line
local function is_visible(str)
@ -55,6 +56,7 @@ describe('window_* functions', function()
insert("epilogue")
win = curwin()
feed('gg')
wait() -- let nvim process the 'gg' command
-- cursor position is at beginning
eq({1, 0}, window('get_cursor', win))

View File

@ -229,11 +229,15 @@ local function curbuf(method, ...)
return buffer(method, buf, ...)
end
local function wait()
session:request('vim_eval', '1')
end
local function curbuf_contents()
-- Before inspecting the buffer, execute 'vim_eval' to wait until all
-- previously sent keys are processed(vim_eval is a deferred function, and
-- only processed after all input)
session:request('vim_eval', '1')
wait()
return table.concat(curbuf('get_line_slice', 0, -1, true, true), '\n')
end
@ -284,5 +288,6 @@ return {
curbuf = curbuf,
curwin = curwin,
curtab = curtab,
curbuf_contents = curbuf_contents
curbuf_contents = curbuf_contents,
wait = wait
}

View File

@ -1,23 +1,44 @@
-- vim: set foldmethod=marker foldmarker=[[,]] :
-- Tests for ":highlight".
local Screen = require('test.functional.ui.screen')
local helpers = require('test.functional.helpers')
local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert
local execute, expect = helpers.execute, helpers.expect
local wait = helpers.wait
describe(':highlight', function()
setup(clear)
it('is working', function()
local screen = Screen.new(35, 10)
screen:attach()
-- Basic test if ":highlight" doesn't crash
execute('highlight')
-- FIXME(tarruda): We need to be sure the prompt is displayed before
-- continuing, or risk a race condition where some of the following input
-- is discarded resulting in test failure
screen:expect([[
:highlight |
SpecialKey xxx ctermfg=4 |
guifg=Blue |
EndOfBuffer xxx links to NonText|
|
NonText xxx ctermfg=12 |
gui=bold |
guifg=Blue |
Directory xxx ctermfg=4 |
-- More --^ |
]])
feed('q')
wait() -- wait until we're back to normal
execute('hi Search')
-- Test setting colors.
-- Test clearing one color and all doesn't generate error or warning
execute('hi NewGroup term=bold cterm=italic ctermfg=DarkBlue ctermbg=Grey gui= guifg=#00ff00 guibg=Cyan')
execute('hi Group2 term= cterm=')
execute('hi Group3 term=underline cterm=bold')
execute('hi NewGroup cterm=italic ctermfg=DarkBlue ctermbg=Grey gui=NONE guifg=#00ff00 guibg=Cyan')
execute('hi Group2 cterm=NONE')
execute('hi Group3 cterm=bold')
execute('redir! @a')
execute('hi NewGroup')
execute('hi Group2')
@ -29,7 +50,7 @@ describe(':highlight', function()
execute('hi Group2')
execute('hi clear')
execute('hi Group3')
execute([[hi Crash term='asdf]])
execute([[hi Crash cterm='asdf]])
execute('redir END')
-- Filter ctermfg and ctermbg, the numbers depend on the terminal
@ -48,11 +69,15 @@ describe(':highlight', function()
expect([[
NewGroup xxx term=bold cterm=italic ctermfg=2 ctermbg=3
NewGroup xxx cterm=italic
ctermfg=2
ctermbg=3
guifg=#00ff00
guibg=Cyan
Group2 xxx cleared
Group3 xxx term=underline cterm=bold
Group3 xxx cterm=bold
NewGroup xxx cleared
@ -65,6 +90,7 @@ describe(':highlight', function()
Group3 xxx cleared
E475: term='asdf]])
E475: cterm='asdf]])
screen:detach()
end)
end)

View File

@ -77,27 +77,7 @@ describe('system()', function()
]])
end)
it('`yes` and is directly interrupted with CTRL-C', function()
feed(':call system("yes")<cr><c-c>')
screen:expect([[
^ |
~ |
~ |
~ |
~ |
~ |
~ |
~ |
~ |
~ |
~ |
~ |
~ |
Type :quit<Enter> to exit Vim |
]])
end)
it('`yes` and is a little bit later interrupted with CTRL-C', function()
it('`yes` and is interrupted with CTRL-C', function()
feed(':call system("yes")<cr>')
feed('<c-c>')
screen:expect([[
@ -247,26 +227,6 @@ describe('systemlist()', function()
]])
end)
it('`yes` and is directly interrupted with CTRL-C', function()
feed(':call systemlist("yes | xargs")<cr><c-c>')
screen:expect([[
^ |
~ |
~ |
~ |
~ |
~ |
~ |
~ |
~ |
~ |
~ |
~ |
~ |
Type :quit<Enter> to exit Vim |
]])
end)
it('`yes` and is a little bit later interrupted with CTRL-C', function()
feed(':call systemlist("yes | xargs")<cr>')
feed('<c-c>')

View File

@ -116,7 +116,11 @@ local debug_screen
local default_screen_timeout = 2500
if os.getenv('VALGRIND') then
default_screen_timeout = 7500
default_screen_timeout = default_screen_timeout * 3
end
if os.getenv('CI_TARGET') then
default_screen_timeout = default_screen_timeout * 3
end
local colors = request('vim_get_color_map')