Merge branch 'master' into hide-container-impl

This commit is contained in:
ZyX 2017-12-16 14:27:41 +03:00
commit 7f3b9a4acc
38 changed files with 457 additions and 157 deletions

View File

@ -6192,7 +6192,6 @@ A jump table for the options with a short description can be found at |Q_op|.
When on, uses |highlight-guifg| and |highlight-guibg| attributes in
the terminal (thus using 24-bit color). Requires a ISO-8613-3
compatible terminal.
Must be set at startup (in your |init.vim| or |--cmd|).
*'terse'* *'noterse'*
'terse' boolean (default off)

View File

@ -80,6 +80,27 @@ Global Events *ui-global*
Some keys are missing in some modes.
["option_set", name, value]
The value of ui related option `name` changed. The sent options are
listed below:
'arabicshape'
'ambiwith'
'emoji'
'guifont'
'guifontset'
'guifontwide'
'showtabline'
'termguicolors'
Options are not added to the list if their effects are already taken
care of. For instance, instead of forwarding the raw 'mouse' option
value, `mouse_on` and `mouse_off` directly indicate if mouse support
is active right now. Some options like 'ambiwith' have already taken
effect on the grid, where appropriate empty cells are added, however
an ui might still use these options when rendering raw text sent from
Nvim, like the text of the cmdline when |ui-ext-cmdline| is set.
["mode_change", mode, mode_idx]
The mode changed. The first parameter `mode` is a string representing
the current mode. `mode_idx` is an index into the array received in

View File

@ -93,6 +93,7 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height,
ui->suspend = remote_ui_suspend;
ui->set_title = remote_ui_set_title;
ui->set_icon = remote_ui_set_icon;
ui->option_set = remote_ui_option_set;
ui->event = remote_ui_event;
memset(ui->ui_ext, 0, sizeof(ui->ui_ext));

View File

@ -58,6 +58,8 @@ void set_title(String title)
FUNC_API_SINCE(3);
void set_icon(String icon)
FUNC_API_SINCE(3);
void option_set(String name, Object value)
FUNC_API_SINCE(4) FUNC_API_BRIDGE_IMPL;
void popupmenu_show(Array items, Integer selected, Integer row, Integer col)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;

View File

@ -1927,7 +1927,7 @@ bool vim_is_ctrl_x_key(int c)
case CTRL_X_EVAL:
return (c == Ctrl_P || c == Ctrl_N);
}
EMSG(_(e_internal));
internal_error("vim_is_ctrl_x_key()");
return false;
}
@ -4681,7 +4681,7 @@ static int ins_complete(int c, bool enable_pum)
line = ml_get(curwin->w_cursor.lnum);
compl_pattern = vim_strnsave(line + compl_col, compl_length);
} else {
EMSG2(_(e_intern2), "ins_complete()");
internal_error("ins_complete()");
return FAIL;
}

View File

@ -1099,10 +1099,11 @@ static void restore_vimvar(int idx, typval_T *save_tv)
vimvars[idx].vv_tv = *save_tv;
if (vimvars[idx].vv_type == VAR_UNKNOWN) {
hi = hash_find(&vimvarht, vimvars[idx].vv_di.di_key);
if (HASHITEM_EMPTY(hi))
EMSG2(_(e_intern2), "restore_vimvar()");
else
if (HASHITEM_EMPTY(hi)) {
internal_error("restore_vimvar()");
} else {
hash_remove(&vimvarht, hi);
}
}
}
@ -1570,7 +1571,7 @@ ex_let_vars (
}
break;
} else if (*arg != ',' && *arg != ']') {
EMSG2(_(e_intern2), "ex_let_vars()");
internal_error("ex_let_vars()");
return FAIL;
}
}
@ -2969,7 +2970,7 @@ int do_unlet(const char *const name, const size_t name_len, const int forceit)
d = di->di_tv.vval.v_dict;
}
if (d == NULL) {
EMSG2(_(e_intern2), "do_unlet()");
internal_error("do_unlet()");
return FAIL;
}
hashitem_T *hi = hash_find(ht, (const char_u *)varname);
@ -7985,7 +7986,7 @@ static void f_empty(typval_T *argvars, typval_T *rettv, FunPtr fptr)
break;
}
case VAR_UNKNOWN: {
EMSG2(_(e_intern2), "f_empty(UNKNOWN)");
internal_error("f_empty(UNKNOWN)");
break;
}
}
@ -17149,7 +17150,7 @@ static void f_type(typval_T *argvars, typval_T *rettv, FunPtr fptr)
break;
}
case VAR_UNKNOWN: {
EMSG2(_(e_intern2), "f_type(UNKNOWN)");
internal_error("f_type(UNKNOWN)");
break;
}
}
@ -18999,7 +19000,7 @@ static void set_var(const char *name, const size_t name_len, typval_T *const tv,
}
return;
} else if (v->di_tv.v_type != tv->v_type) {
EMSG2(_(e_intern2), "set_var()");
internal_error("set_var()");
}
}
@ -19266,7 +19267,7 @@ int var_item_copy(const vimconv_T *const conv,
}
break;
case VAR_UNKNOWN:
EMSG2(_(e_intern2), "var_item_copy(UNKNOWN)");
internal_error("var_item_copy(UNKNOWN)");
ret = FAIL;
}
--recurse;
@ -20954,11 +20955,11 @@ void func_unref(char_u *name)
if (fp == NULL && isdigit(*name)) {
#ifdef EXITFREE
if (!entered_free_all_mem) {
EMSG2(_(e_intern2), "func_unref()");
internal_error("func_unref()");
abort();
}
#else
EMSG2(_(e_intern2), "func_unref()");
internal_error("func_unref()");
abort();
#endif
}
@ -20997,7 +20998,7 @@ void func_ref(char_u *name)
} else if (isdigit(*name)) {
// Only give an error for a numbered function.
// Fail silently, when named or lambda function isn't found.
EMSG2(_(e_intern2), "func_ref()");
internal_error("func_ref()");
}
}

View File

@ -346,7 +346,7 @@ int encode_read_from_list(ListReaderState *const state, char *const buf,
do { \
const char *const fun_ = (const char *)(fun); \
if (fun_ == NULL) { \
EMSG2(_(e_intern2), "string(): NULL function name"); \
internal_error("string(): NULL function name"); \
ga_concat(gap, "function(NULL"); \
} else { \
ga_concat(gap, "function("); \

View File

@ -1880,14 +1880,20 @@ static inline void _nothing_conv_func_end(typval_T *const tv, const int copyID)
tv->v_lock = VAR_UNLOCKED; \
} while (0)
static inline void _nothing_conv_empty_dict(typval_T *const tv,
dict_T **const dictp)
FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ARG(2)
{
tv_dict_unref(*dictp);
*dictp = NULL;
if (tv != NULL) {
tv->v_lock = VAR_UNLOCKED;
}
}
#define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \
do { \
assert((void *)&dict != (void *)&TYPVAL_ENCODE_NODICT_VAR); \
tv_dict_unref((dict_T *)dict); \
*((dict_T **)&dict) = NULL; \
if (tv != NULL) { \
((typval_T *)tv)->v_lock = VAR_UNLOCKED; \
} \
_nothing_conv_empty_dict(tv, ((dict_T **)&dict)); \
} while (0)
static inline int _nothing_conv_real_list_after_start(

View File

@ -624,7 +624,7 @@ _convert_one_value_regular_dict: {}
break;
}
case VAR_UNKNOWN: {
EMSG2(_(e_intern2), STR(_TYPVAL_ENCODE_CONVERT_ONE_VALUE) "()");
internal_error(STR(_TYPVAL_ENCODE_CONVERT_ONE_VALUE) "()");
return FAIL;
}
}

View File

@ -517,7 +517,7 @@ static void discard_exception(except_T *excp, int was_finished)
char_u *saved_IObuff;
if (excp == NULL) {
EMSG(_(e_internal));
internal_error("discard_exception()");
return;
}
@ -619,8 +619,9 @@ static void catch_exception(except_T *excp)
*/
static void finish_exception(except_T *excp)
{
if (excp != caught_stack)
EMSG(_(e_internal));
if (excp != caught_stack) {
internal_error("finish_exception()");
}
caught_stack = caught_stack->caught;
if (caught_stack != NULL) {
set_vim_var_string(VV_EXCEPTION, (char *) caught_stack->value, -1);
@ -1422,8 +1423,9 @@ void ex_catch(exarg_T *eap)
* ":endtry" or when the catch clause is left by a ":continue",
* ":break", ":return", ":finish", error, interrupt, or another
* exception. */
if (cstack->cs_exception[cstack->cs_idx] != current_exception)
EMSG(_(e_internal));
if (cstack->cs_exception[cstack->cs_idx] != current_exception) {
internal_error("ex_catch()");
}
} else {
/*
* If there is a preceding catch clause and it caught the exception,
@ -1547,7 +1549,7 @@ void ex_finally(exarg_T *eap)
* exception will be discarded. */
if (did_throw && cstack->cs_exception[cstack->cs_idx]
!= current_exception)
EMSG(_(e_internal));
internal_error("ex_finally()");
}
/*

View File

@ -37,7 +37,7 @@ function write_arglist(output, ev, need_copy)
for j = 1, #ev.parameters do
local param = ev.parameters[j]
local kind = string.upper(param[1])
local do_copy = need_copy and (kind == "ARRAY" or kind == "DICTIONARY" or kind == "STRING")
local do_copy = need_copy and (kind == "ARRAY" or kind == "DICTIONARY" or kind == "STRING" or kind == "OBJECT")
output:write(' ADD(args, ')
if do_copy then
output:write('copy_object(')
@ -91,7 +91,7 @@ for i = 1, #events do
recv_cleanup = recv_cleanup..' api_free_string('..param[2]..');\n'
argc = argc+2
elseif param[1] == 'Array' then
send = send..' Array copy_'..param[2]..' = copy_array('..param[2]..');\n'
send = send..' Array '..copy..' = copy_array('..param[2]..');\n'
argv = argv..', '..copy..'.items, INT2PTR('..copy..'.size)'
recv = (recv..' Array '..param[2]..
' = (Array){.items = argv['..argc..'],'..
@ -99,6 +99,15 @@ for i = 1, #events do
recv_argv = recv_argv..', '..param[2]
recv_cleanup = recv_cleanup..' api_free_array('..param[2]..');\n'
argc = argc+2
elseif param[1] == 'Object' then
send = send..' Object *'..copy..' = xmalloc(sizeof(Object));\n'
send = send..' *'..copy..' = copy_object('..param[2]..');\n'
argv = argv..', '..copy
recv = recv..' Object '..param[2]..' = *(Object *)argv['..argc..'];\n'
recv_argv = recv_argv..', '..param[2]
recv_cleanup = (recv_cleanup..' api_free_object('..param[2]..');\n'..
' xfree(argv['..argc..']);\n')
argc = argc+1
elseif param[1] == 'Integer' or param[1] == 'Boolean' then
argv = argv..', INT2PTR('..param[2]..')'
recv_argv = recv_argv..', PTR2INT(argv['..argc..'])'
@ -119,7 +128,7 @@ for i = 1, #events do
write_signature(bridge_output, ev, 'UI *ui')
bridge_output:write('\n{\n')
bridge_output:write(send)
bridge_output:write(' UI_BRIDGE_CALL(ui, '..ev.name..', '..argc..', ui'..argv..');\n}\n')
bridge_output:write(' UI_BRIDGE_CALL(ui, '..ev.name..', '..argc..', ui'..argv..');\n}\n\n')
end
end

View File

@ -36,6 +36,7 @@ local redraw_flags={
all_windows='P_RALL',
everything='P_RCLR',
curswant='P_CURSWANT',
ui_option='P_UI_OPTION',
}
local list_flags={

View File

@ -254,16 +254,17 @@ static void add_buff(buffheader_T *const buf, const char *const s,
return;
}
if (buf->bh_first.b_next == NULL) { /* first add to list */
if (buf->bh_first.b_next == NULL) { // first add to list
buf->bh_space = 0;
buf->bh_curr = &(buf->bh_first);
} else if (buf->bh_curr == NULL) { /* buffer has already been read */
EMSG(_("E222: Add to read buffer"));
} else if (buf->bh_curr == NULL) { // buffer has already been read
IEMSG(_("E222: Add to read buffer"));
return;
} else if (buf->bh_index != 0)
} else if (buf->bh_index != 0) {
memmove(buf->bh_first.b_next->b_str,
buf->bh_first.b_next->b_str + buf->bh_index,
STRLEN(buf->bh_first.b_next->b_str + buf->bh_index) + 1);
buf->bh_first.b_next->b_str + buf->bh_index,
STRLEN(buf->bh_first.b_next->b_str + buf->bh_index) + 1);
}
buf->bh_index = 0;
size_t len;
@ -1152,14 +1153,16 @@ void alloc_typebuf(void)
*/
void free_typebuf(void)
{
if (typebuf.tb_buf == typebuf_init)
EMSG2(_(e_intern2), "Free typebuf 1");
else
if (typebuf.tb_buf == typebuf_init) {
internal_error("Free typebuf 1");
} else {
xfree(typebuf.tb_buf);
if (typebuf.tb_noremap == noremapbuf_init)
EMSG2(_(e_intern2), "Free typebuf 2");
else
}
if (typebuf.tb_noremap == noremapbuf_init) {
internal_error("Free typebuf 2");
} else {
xfree(typebuf.tb_noremap);
}
}
/*
@ -3905,7 +3908,7 @@ makemap (
c1 = 't';
break;
default:
EMSG(_("E228: makemap: Illegal mode"));
IEMSG(_("E228: makemap: Illegal mode"));
return FAIL;
}
do { /* do this twice if c2 is set, 3 times with c3 */

View File

@ -1043,6 +1043,7 @@ EXTERN char_u e_for[] INIT(= N_("E588: :endfor without :for"));
EXTERN char_u e_exists[] INIT(= N_("E13: File exists (add ! to override)"));
EXTERN char_u e_failed[] INIT(= N_("E472: Command failed"));
EXTERN char_u e_internal[] INIT(= N_("E473: Internal error"));
EXTERN char_u e_intern2[] INIT(= N_("E685: Internal error: %s"));
EXTERN char_u e_interr[] INIT(= N_("Interrupted"));
EXTERN char_u e_invaddr[] INIT(= N_("E14: Invalid address"));
EXTERN char_u e_invarg[] INIT(= N_("E474: Invalid argument"));
@ -1134,7 +1135,6 @@ EXTERN char_u e_write[] INIT(= N_("E80: Error while writing"));
EXTERN char_u e_zerocount[] INIT(= N_("E939: Positive count required"));
EXTERN char_u e_usingsid[] INIT(= N_(
"E81: Using <SID> not in a script context"));
EXTERN char_u e_intern2[] INIT(= N_("E685: Internal error: %s"));
EXTERN char_u e_maxmempat[] INIT(= N_(
"E363: pattern uses more memory than 'maxmempattern'"));
EXTERN char_u e_emptybuf[] INIT(= N_("E749: empty buffer"));

View File

@ -208,7 +208,7 @@ int hash_add(hashtab_T *ht, char_u *key)
hash_T hash = hash_hash(key);
hashitem_T *hi = hash_lookup(ht, (const char *)key, STRLEN(key), hash);
if (!HASHITEM_EMPTY(hi)) {
EMSG2(_(e_intern2), "hash_add()");
internal_error("hash_add()");
return FAIL;
}
hash_add_item(ht, hi, key, hash);

View File

@ -376,8 +376,9 @@ void mf_put(memfile_T *mfp, bhdr_T *hp, bool dirty, bool infile)
{
unsigned flags = hp->bh_flags;
if ((flags & BH_LOCKED) == 0)
EMSG(_("E293: block was not locked"));
if ((flags & BH_LOCKED) == 0) {
IEMSG(_("E293: block was not locked"));
}
flags &= ~BH_LOCKED;
if (dirty) {
flags |= BH_DIRTY;

View File

@ -293,7 +293,7 @@ int ml_open(buf_T *buf)
*/
hp = mf_new(mfp, false, 1);
if (hp->bh_bnum != 0) {
EMSG(_("E298: Didn't get block nr 0?"));
IEMSG(_("E298: Didn't get block nr 0?"));
goto error;
}
b0p = hp->bh_data;
@ -335,7 +335,7 @@ int ml_open(buf_T *buf)
if ((hp = ml_new_ptr(mfp)) == NULL)
goto error;
if (hp->bh_bnum != 1) {
EMSG(_("E298: Didn't get block nr 1?"));
IEMSG(_("E298: Didn't get block nr 1?"));
goto error;
}
pp = hp->bh_data;
@ -351,7 +351,7 @@ int ml_open(buf_T *buf)
*/
hp = ml_new_data(mfp, FALSE, 1);
if (hp->bh_bnum != 2) {
EMSG(_("E298: Didn't get block nr 2?"));
IEMSG(_("E298: Didn't get block nr 2?"));
goto error;
}
@ -635,13 +635,14 @@ static void ml_upd_block0(buf_T *buf, upd_block0_T what)
if (mfp == NULL || (hp = mf_get(mfp, 0, 1)) == NULL)
return;
b0p = hp->bh_data;
if (ml_check_b0_id(b0p) == FAIL)
EMSG(_("E304: ml_upd_block0(): Didn't get block 0??"));
else {
if (what == UB_FNAME)
if (ml_check_b0_id(b0p) == FAIL) {
IEMSG(_("E304: ml_upd_block0(): Didn't get block 0??"));
} else {
if (what == UB_FNAME) {
set_b0_fname(b0p, buf);
else /* what == UB_SAME_DIR */
} else { // what == UB_SAME_DIR
set_b0_dir_flag(b0p, buf);
}
}
mf_put(mfp, hp, true, false);
}
@ -1742,11 +1743,11 @@ ml_get_buf (
if (lnum > buf->b_ml.ml_line_count) { /* invalid line number */
if (recursive == 0) {
/* Avoid giving this message for a recursive call, may happen when
* the GUI redraws part of the text. */
++recursive;
EMSGN(_("E315: ml_get: invalid lnum: %" PRId64), lnum);
--recursive;
// Avoid giving this message for a recursive call, may happen when
// the GUI redraws part of the text.
recursive++;
IEMSGN(_("E315: ml_get: invalid lnum: %" PRId64), lnum);
recursive--;
}
errorret:
STRCPY(IObuff, "???");
@ -1774,11 +1775,11 @@ errorret:
*/
if ((hp = ml_find_line(buf, lnum, ML_FIND)) == NULL) {
if (recursive == 0) {
/* Avoid giving this message for a recursive call, may happen
* when the GUI redraws part of the text. */
++recursive;
EMSGN(_("E316: ml_get: cannot find line %" PRId64), lnum);
--recursive;
// Avoid giving this message for a recursive call, may happen
// when the GUI redraws part of the text.
recursive++;
IEMSGN(_("E316: ml_get: cannot find line %" PRId64), lnum);
recursive--;
}
goto errorret;
}
@ -2162,7 +2163,7 @@ ml_append_int (
return FAIL;
pp = hp->bh_data; /* must be pointer block */
if (pp->pb_id != PTR_ID) {
EMSG(_("E317: pointer block id wrong 3"));
IEMSG(_("E317: pointer block id wrong 3"));
mf_put(mfp, hp, false, false);
return FAIL;
}
@ -2295,8 +2296,8 @@ ml_append_int (
* Safety check: fallen out of for loop?
*/
if (stack_idx < 0) {
EMSG(_("E318: Updated too many blocks?"));
buf->b_ml.ml_stack_top = 0; /* invalidate stack */
IEMSG(_("E318: Updated too many blocks?"));
buf->b_ml.ml_stack_top = 0; // invalidate stack
}
}
@ -2435,7 +2436,7 @@ static int ml_delete_int(buf_T *buf, linenr_T lnum, int message)
return FAIL;
pp = hp->bh_data; /* must be pointer block */
if (pp->pb_id != PTR_ID) {
EMSG(_("E317: pointer block id wrong 4"));
IEMSG(_("E317: pointer block id wrong 4"));
mf_put(mfp, hp, false, false);
return FAIL;
}
@ -2630,9 +2631,9 @@ static void ml_flush_line(buf_T *buf)
new_line = buf->b_ml.ml_line_ptr;
hp = ml_find_line(buf, lnum, ML_FIND);
if (hp == NULL)
EMSGN(_("E320: Cannot find line %" PRId64), lnum);
else {
if (hp == NULL) {
IEMSGN(_("E320: Cannot find line %" PRId64), lnum);
} else {
dp = hp->bh_data;
idx = lnum - buf->b_ml.ml_locked_low;
start = ((dp->db_index[idx]) & DB_INDEX_MASK);
@ -2841,7 +2842,7 @@ static bhdr_T *ml_find_line(buf_T *buf, linenr_T lnum, int action)
pp = (PTR_BL *)(dp); /* must be pointer block */
if (pp->pb_id != PTR_ID) {
EMSG(_("E317: pointer block id wrong"));
IEMSG(_("E317: pointer block id wrong"));
goto error_block;
}
@ -2878,13 +2879,14 @@ static bhdr_T *ml_find_line(buf_T *buf, linenr_T lnum, int action)
break;
}
}
if (idx >= (int)pp->pb_count) { /* past the end: something wrong! */
if (lnum > buf->b_ml.ml_line_count)
EMSGN(_("E322: line number out of range: %" PRId64 " past the end"),
lnum - buf->b_ml.ml_line_count);
if (idx >= (int)pp->pb_count) { // past the end: something wrong!
if (lnum > buf->b_ml.ml_line_count) {
IEMSGN(_("E322: line number out of range: %" PRId64 " past the end"),
lnum - buf->b_ml.ml_line_count);
else
EMSGN(_("E323: line count wrong in block %" PRId64), bnum);
} else {
IEMSGN(_("E323: line count wrong in block %" PRId64), bnum);
}
goto error_block;
}
if (action == ML_DELETE) {
@ -2960,7 +2962,7 @@ static void ml_lineadd(buf_T *buf, int count)
pp = hp->bh_data; /* must be pointer block */
if (pp->pb_id != PTR_ID) {
mf_put(mfp, hp, false, false);
EMSG(_("E317: pointer block id wrong 2"));
IEMSG(_("E317: pointer block id wrong 2"));
break;
}
pp->pb_pointer[ip->ip_index].pe_line_count += count;

View File

@ -582,20 +582,61 @@ void emsg_invreg(int name)
/// Print an error message with unknown number of arguments
bool emsgf(const char *const fmt, ...)
{
bool ret;
va_list ap;
va_start(ap, fmt);
ret = emsgfv(fmt, ap);
va_end(ap);
return ret;
}
/// Print an error message with unknown number of arguments
static bool emsgfv(const char *fmt, va_list ap)
{
static char errbuf[IOSIZE];
if (emsg_not_now()) {
return true;
}
va_list ap;
va_start(ap, fmt);
vim_vsnprintf(errbuf, sizeof(errbuf), fmt, ap, NULL);
va_end(ap);
return emsg((const char_u *)errbuf);
}
/// Same as emsg(...), but abort on error when ABORT_ON_INTERNAL_ERROR is
/// defined. It is used for internal errors only, so that they can be
/// detected when fuzzing vim.
void iemsg(const char *s)
{
msg((char_u *)s);
#ifdef ABORT_ON_INTERNAL_ERROR
abort();
#endif
}
/// Same as emsgf(...) but abort on error when ABORT_ON_INTERNAL_ERROR is
/// defined. It is used for internal errors only, so that they can be
/// detected when fuzzing vim.
void iemsgf(const char *s, ...)
{
va_list ap;
va_start(ap, s);
(void)emsgfv(s, ap);
va_end(ap);
#ifdef ABORT_ON_INTERNAL_ERROR
abort();
#endif
}
/// Give an "Internal error" message.
void internal_error(char *where)
{
IEMSG2(_(e_intern2), where);
}
static void msg_emsgf_event(void **argv)
{
char *s = argv[0];

View File

@ -49,6 +49,15 @@
/// Like #EMSG, but for messages with one "%" PRIu64 inside
#define EMSGU(s, n) emsgf((const char *) (s), (uint64_t)(n))
/// Like #EMSG, but for internal messages
#define IEMSG(s) iemsg((const char *)(s))
/// Like #EMSG2, but for internal messages
#define IEMSG2(s, p) iemsgf((const char *)(s), (p))
/// Like #EMSGN, but for internal messages
#define IEMSGN(s, n) iemsgf((const char *)(s), (int64_t)(n))
/// Display message at the recorded position
#define MSG_PUTS(s) msg_puts((const char *)(s))

View File

@ -74,6 +74,7 @@
#include "nvim/undo.h"
#include "nvim/window.h"
#include "nvim/os/os.h"
#include "nvim/api/private/helpers.h"
#include "nvim/os/input.h"
#include "nvim/os/lang.h"
@ -248,6 +249,7 @@ typedef struct vimoption {
#define P_RWINONLY 0x10000000U ///< only redraw current window
#define P_NDNAME 0x20000000U ///< only normal dir name chars allowed
#define P_UI_OPTION 0x40000000U ///< send option to remote ui
#define HIGHLIGHT_INIT \
"8:SpecialKey,~:EndOfBuffer,z:TermCursor,Z:TermCursorNC,@:NonText," \
@ -1188,6 +1190,7 @@ do_set (
set_options_default(OPT_FREE | opt_flags);
didset_options();
didset_options2();
ui_refresh_options();
redraw_all_later(CLEAR);
} else {
showoptions(1, opt_flags);
@ -1815,6 +1818,10 @@ do_set (
NULL, false, NULL);
reset_v_option_vars();
xfree(saved_origval);
if (options[opt_idx].flags & P_UI_OPTION) {
ui_call_option_set(cstr_as_string(options[opt_idx].fullname),
STRING_OBJ(cstr_as_string(*(char **)varp)));
}
}
} else {
// key code option(FIXME(tarruda): Show a warning or something
@ -2251,7 +2258,7 @@ int was_set_insecurely(char_u *opt, int opt_flags)
uint32_t *flagp = insecure_flag(idx, opt_flags);
return (*flagp & P_INSECURE) != 0;
}
EMSG2(_(e_intern2), "was_set_insecurely()");
internal_error("was_set_insecurely()");
return -1;
}
@ -2310,8 +2317,8 @@ set_string_option_direct (
if (idx == -1) { // Use name.
idx = findoption((const char *)name);
if (idx < 0) { // Not found (should not happen).
EMSG2(_(e_intern2), "set_string_option_direct()");
EMSG2(_("For option %s"), name);
internal_error("set_string_option_direct()");
IEMSG2(_("For option %s"), name);
return;
}
}
@ -2417,6 +2424,10 @@ static char *set_string_option(const int opt_idx, const char *const value,
NULL, false, NULL);
reset_v_option_vars();
xfree(saved_oldval);
if (options[opt_idx].flags & P_UI_OPTION) {
ui_call_option_set(cstr_as_string(options[opt_idx].fullname),
STRING_OBJ(cstr_as_string((char *)(*varp))));
}
}
return r;
@ -4024,6 +4035,10 @@ static char *set_bool_option(const int opt_idx, char_u *const varp,
(char_u *) options[opt_idx].fullname,
NULL, false, NULL);
reset_v_option_vars();
if (options[opt_idx].flags & P_UI_OPTION) {
ui_call_option_set(cstr_as_string(options[opt_idx].fullname),
BOOLEAN_OBJ(value));
}
}
comp_col(); /* in case 'ruler' or 'showcmd' changed */
@ -4429,6 +4444,10 @@ static char *set_num_option(int opt_idx, char_u *varp, long value,
(char_u *) options[opt_idx].fullname,
NULL, false, NULL);
reset_v_option_vars();
if (options[opt_idx].flags & P_UI_OPTION) {
ui_call_option_set(cstr_as_string(options[opt_idx].fullname),
INTEGER_OBJ(value));
}
}
comp_col(); /* in case 'columns' or 'ls' changed */
@ -4999,6 +5018,29 @@ static int optval_default(vimoption_T *p, char_u *varp)
return STRCMP(*(char_u **)varp, p->def_val[dvi]) == 0;
}
/// Send update to UIs with values of UI relevant options
void ui_refresh_options(void)
{
for (int opt_idx = 0; options[opt_idx].fullname; opt_idx++) {
uint32_t flags = options[opt_idx].flags;
if (!(flags & P_UI_OPTION)) {
continue;
}
String name = cstr_as_string(options[opt_idx].fullname);
void *varp = options[opt_idx].var;
Object value = OBJECT_INIT;
if (flags & P_BOOL) {
value = BOOLEAN_OBJ(*(int *)varp);
} else if (flags & P_NUM) {
value = INTEGER_OBJ(*(long *)varp);
} else if (flags & P_STRING) {
// cstr_as_string handles NULL string
value = STRING_OBJ(cstr_as_string(*(char **)varp));
}
ui_call_option_set(name, value);
}
}
/*
* showoneopt: show the value of one option
* must not be called with a hidden option!
@ -5542,7 +5584,7 @@ static char_u *get_varp(vimoption_T *p)
case PV_KMAP: return (char_u *)&(curbuf->b_p_keymap);
case PV_SCL: return (char_u *)&(curwin->w_p_scl);
case PV_WINHL: return (char_u *)&(curwin->w_p_winhl);
default: EMSG(_("E356: get_varp ERROR"));
default: IEMSG(_("E356: get_varp ERROR"));
}
/* always return a valid pointer to avoid a crash! */
return (char_u *)&(curbuf->b_p_wm);

View File

@ -447,6 +447,9 @@ EXTERN char_u *p_popt; // 'printoptions'
EXTERN char_u *p_header; // 'printheader'
EXTERN int p_prompt; // 'prompt'
EXTERN char_u *p_guicursor; // 'guicursor'
EXTERN char_u *p_guifont; // 'guifont'
EXTERN char_u *p_guifontset; // 'guifontset'
EXTERN char_u *p_guifontwide; // 'guifontwide'
EXTERN char_u *p_hf; // 'helpfile'
EXTERN long p_hh; // 'helpheight'
EXTERN char_u *p_hlg; // 'helplang'

View File

@ -68,7 +68,8 @@ return {
type='bool', scope={'global'},
vi_def=true,
vim=true,
redraw={'everything'},
redraw={'everything', 'ui_option'},
varname='p_arshape',
defaults={if_true={vi=true}}
},
@ -91,7 +92,7 @@ return {
full_name='ambiwidth', abbreviation='ambw',
type='string', scope={'global'},
vi_def=true,
redraw={'everything'},
redraw={'everything', 'ui_option'},
varname='p_ambw',
defaults={if_true={vi="single"}}
},
@ -661,7 +662,7 @@ return {
full_name='emoji', abbreviation='emo',
type='bool', scope={'global'},
vi_def=true,
redraw={'everything'},
redraw={'everything', 'ui_option'},
varname='p_emoji',
defaults={if_true={vi=true}}
},
@ -1021,23 +1022,26 @@ return {
type='string', list='onecomma', scope={'global'},
deny_duplicates=true,
vi_def=true,
redraw={'everything'},
enable_if=false,
varname='p_guifont',
redraw={'everything', 'ui_option'},
defaults={if_true={vi=""}}
},
{
full_name='guifontset', abbreviation='gfs',
type='string', list='onecomma', scope={'global'},
vi_def=true,
redraw={'everything'},
enable_if=false,
varname='p_guifontset',
redraw={'everything', 'ui_option'},
defaults={if_true={vi=""}}
},
{
full_name='guifontwide', abbreviation='gfw',
type='string', list='onecomma', scope={'global'},
deny_duplicates=true,
vi_def=true,
redraw={'everything'},
enable_if=false,
redraw={'everything', 'ui_option'},
varname='p_guifontwide',
defaults={if_true={vi=""}}
},
{
full_name='guioptions', abbreviation='go',
@ -2164,7 +2168,7 @@ return {
full_name='showtabline', abbreviation='stal',
type='number', scope={'global'},
vi_def=true,
redraw={'all_windows'},
redraw={'all_windows', 'ui_option'},
varname='p_stal',
defaults={if_true={vi=1}}
},
@ -2435,7 +2439,7 @@ return {
full_name='termguicolors', abbreviation='tgc',
type='bool', scope={'global'},
vi_def=false,
redraw={'everything'},
redraw={'everything', 'ui_option'},
varname='p_tgc',
defaults={if_true={vi=false}}
},
@ -2505,11 +2509,10 @@ return {
full_name='titleold',
type='string', scope={'global'},
secure=true,
gettext=true,
no_mkrc=true,
vi_def=true,
varname='p_titleold',
defaults={if_true={vi=N_("")}}
defaults={if_true={vi=""}}
},
{
full_name='titlestring',

View File

@ -887,7 +887,7 @@ bool os_setenv_append_path(const char *fname)
# define MAX_ENVPATHLEN INT_MAX
#endif
if (!path_is_absolute_path((char_u *)fname)) {
EMSG2(_(e_intern2), "os_setenv_append_path()");
internal_error("os_setenv_append_path()");
return false;
}
const char *tail = (char *)path_tail_with_sep((char_u *)fname);

View File

@ -72,8 +72,7 @@ int pty_process_spawn(PtyProcess *ptyproc)
ELOG("forkpty failed: %s", strerror(errno));
return status;
} else if (pid == 0) {
init_child(ptyproc);
abort();
init_child(ptyproc); // never returns
}
// make sure the master file descriptor is non blocking
@ -163,14 +162,15 @@ static void init_child(PtyProcess *ptyproc) FUNC_ATTR_NONNULL_ALL
Process *proc = (Process *)ptyproc;
if (proc->cwd && os_chdir(proc->cwd) != 0) {
fprintf(stderr, "chdir failed: %s\n", strerror(errno));
ELOG("chdir failed: %s", strerror(errno));
return;
}
char *prog = ptyproc->process.argv[0];
setenv("TERM", ptyproc->term_name ? ptyproc->term_name : "ansi", 1);
execvp(prog, ptyproc->process.argv);
fprintf(stderr, "execvp failed: %s: %s\n", strerror(errno), prog);
ELOG("execvp failed: %s: %s", strerror(errno), prog);
_exit(122); // 122 is EXEC_FAILED in the Vim source.
}
static void init_termios(struct termios *termios) FUNC_ATTR_NONNULL_ALL

View File

@ -10,6 +10,7 @@
#endif
#include "nvim/ascii.h"
#include "nvim/log.h"
#include "nvim/vim.h"
#include "nvim/globals.h"
#include "nvim/memline.h"
@ -162,7 +163,7 @@ static void on_signal(SignalWatcher *handle, int signum, void *data)
}
break;
default:
fprintf(stderr, "Invalid signal %d", signum);
ELOG("invalid signal: %d", signum);
break;
}
}

View File

@ -2856,7 +2856,7 @@ static void qf_fill_buffer(qf_info_T *qi, buf_T *buf, qfline_T *old_last)
if (old_last == NULL) {
if (buf != curbuf) {
EMSG2(_(e_intern2), "qf_fill_buffer()");
internal_error("qf_fill_buffer()");
return;
}

View File

@ -458,16 +458,13 @@ static int toggle_Magic(int x)
/* Used for an error (down from) vim_regcomp(): give the error message, set
* rc_did_emsg and return NULL */
#define EMSG_RET_NULL(m) return (EMSG(m), rc_did_emsg = TRUE, (void *)NULL)
#define EMSG_RET_FAIL(m) return (EMSG(m), rc_did_emsg = TRUE, FAIL)
#define EMSG2_RET_NULL(m, \
c) return (EMSG2((m), \
(c) ? "" : "\\"), rc_did_emsg = TRUE, \
(void *)NULL)
#define EMSG2_RET_FAIL(m, \
c) return (EMSG2((m), \
(c) ? "" : "\\"), rc_did_emsg = TRUE, \
FAIL)
#define EMSG_RET_NULL(m) return (EMSG(m), rc_did_emsg = true, (void *)NULL)
#define IEMSG_RET_NULL(m) return (IEMSG(m), rc_did_emsg = true, (void *)NULL)
#define EMSG_RET_FAIL(m) return (EMSG(m), rc_did_emsg = true, FAIL)
#define EMSG2_RET_NULL(m, c) \
return (EMSG2((m), (c) ? "" : "\\"), rc_did_emsg = true, (void *)NULL)
#define EMSG2_RET_FAIL(m, c) \
return (EMSG2((m), (c) ? "" : "\\"), rc_did_emsg = true, FAIL)
#define EMSG_ONE_RET_NULL EMSG2_RET_NULL(_( \
"E369: invalid item in %s%%[]"), reg_magic == MAGIC_ALL)
@ -1891,8 +1888,8 @@ static char_u *regatom(int *flagp)
case Magic(')'):
if (one_exactly)
EMSG_ONE_RET_NULL;
EMSG_RET_NULL(_(e_internal)); /* Supposed to be caught earlier. */
/* NOTREACHED */
IEMSG_RET_NULL(_(e_internal)); // Supposed to be caught earlier.
// NOTREACHED
case Magic('='):
case Magic('?'):
@ -4534,7 +4531,7 @@ regmatch (
brace_max[no] = OPERAND_MAX(scan);
brace_count[no] = 0;
} else {
EMSG(_(e_internal)); /* Shouldn't happen */
internal_error("BRACE_LIMITS");
status = RA_FAIL;
}
}

View File

@ -5354,7 +5354,7 @@ add_sound_suggest (
// Find the word nr in the soundfold tree.
sfwordnr = soundfold_find(slang, goodword);
if (sfwordnr < 0) {
EMSG2(_(e_intern2), "add_sound_suggest()");
internal_error("add_sound_suggest()");
return;
}

View File

@ -70,6 +70,7 @@ typedef struct {
UIBridgeData *bridge;
Loop *loop;
bool stop;
uv_timer_t after_startup_timer;
unibi_var_t params[9];
char buf[OUTBUF_SIZE];
size_t bufpos;
@ -151,6 +152,7 @@ UI *tui_start(void)
ui->suspend = tui_suspend;
ui->set_title = tui_set_title;
ui->set_icon = tui_set_icon;
ui->option_set= tui_option_set;
ui->event = tui_event;
memset(ui->ui_ext, 0, sizeof(ui->ui_ext));
@ -168,24 +170,6 @@ static size_t unibi_pre_fmt_str(TUIData *data, unsigned int unibi_index,
return unibi_run(str, data->params, buf, len);
}
/// Emits some termcodes after Nvim startup, which were observed to slowdown
/// rendering during startup in tmux 2.3 (+focus-events). #7649
static void terminfo_after_startup_event(void **argv)
{
UI *ui = argv[0];
bool defer = argv[1] != NULL; // clever(?) boolean without malloc() dance.
TUIData *data = ui->data;
if (defer) { // We're on the main-loop. Now forward to the TUI loop.
loop_schedule(data->loop,
event_create(terminfo_after_startup_event, 2, ui, NULL));
return;
}
// Enable bracketed paste
unibi_out_ext(ui, data->unibi_ext.enable_bracketed_paste);
// Enable focus reporting
unibi_out_ext(ui, data->unibi_ext.enable_focus_reporting);
}
static void termname_set_event(void **argv)
{
char *termname = argv[0];
@ -265,6 +249,9 @@ static void terminfo_start(UI *ui)
unibi_out(ui, unibi_enter_ca_mode);
unibi_out(ui, unibi_keypad_xmit);
unibi_out(ui, unibi_clear_screen);
// Enable bracketed paste
unibi_out_ext(ui, data->unibi_ext.enable_bracketed_paste);
uv_loop_init(&data->write_loop);
if (data->out_isatty) {
uv_tty_init(&data->write_loop, &data->output_handle.tty, data->out_fd, 0);
@ -277,9 +264,6 @@ static void terminfo_start(UI *ui)
uv_pipe_init(&data->write_loop, &data->output_handle.pipe, 0);
uv_pipe_open(&data->output_handle.pipe, data->out_fd);
}
loop_schedule(&main_loop,
event_create(terminfo_after_startup_event, 2, ui, ui));
}
static void terminfo_stop(UI *ui)
@ -307,6 +291,18 @@ static void terminfo_stop(UI *ui)
unibi_destroy(data->ut);
}
static void after_startup_timer_cb(uv_timer_t *handle)
FUNC_ATTR_NONNULL_ALL
{
UI *ui = handle->data;
TUIData *data = ui->data;
uv_timer_stop(&data->after_startup_timer);
// Emit this after Nvim startup, not during. This works around a tmux
// 2.3 bug(?) which caused slow drawing during startup. #7649
unibi_out_ext(ui, data->unibi_ext.enable_focus_reporting);
}
static void tui_terminal_start(UI *ui)
{
TUIData *data = ui->data;
@ -316,6 +312,8 @@ static void tui_terminal_start(UI *ui)
update_size(ui);
signal_watcher_start(&data->winch_handle, sigwinch_cb, SIGWINCH);
term_input_start(&data->input);
uv_timer_start(&data->after_startup_timer, after_startup_timer_cb, 500, 0);
}
static void tui_terminal_stop(UI *ui)
@ -349,6 +347,8 @@ static void tui_main(UIBridgeData *bridge, UI *ui)
#ifdef UNIX
signal_watcher_start(&data->cont_handle, sigcont_cb, SIGCONT);
#endif
uv_timer_init(&data->loop->uv, &data->after_startup_timer);
data->after_startup_timer.data = ui;
#if TERMKEY_VERSION_MAJOR > 0 || TERMKEY_VERSION_MINOR > 18
data->input.tk_ti_hook_fn = tui_tk_ti_getstr;
@ -367,6 +367,7 @@ static void tui_main(UIBridgeData *bridge, UI *ui)
loop_poll_events(&tui_loop, -1); // tui_loop.events is never processed
}
uv_close((uv_handle_t *)&data->after_startup_timer, NULL);
ui_bridge_stopped(bridge);
term_input_destroy(&data->input);
signal_watcher_stop(&data->cont_handle);
@ -1136,6 +1137,14 @@ static void tui_set_icon(UI *ui, String icon)
{
}
static void tui_option_set(UI *ui, String name, Object value)
{
if (strequal(name.data, "termguicolors")) {
// NB: value for bridge is set in ui_bridge.c
ui->rgb = value.data.boolean;
}
}
// NB: if we start to use this, the ui_bridge must be updated
// to make a copy for the tui thread
static void tui_event(UI *ui, char *name, Array args, bool *args_consumed)

View File

@ -339,6 +339,7 @@ void ui_attach_impl(UI *ui)
}
uis[ui_count++] = ui;
ui_refresh_options();
ui_refresh();
}

View File

@ -31,11 +31,9 @@ struct ui_t {
bool ui_ext[UI_WIDGETS]; ///< Externalized widgets
int width, height;
void *data;
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ui_events.generated.h"
#endif
void (*event)(UI *ui, char *name, Array args, bool *args_consumed);
void (*stop)(UI *ui);
};

View File

@ -66,6 +66,7 @@ UI *ui_bridge_attach(UI *ui, ui_main_fn ui_main, event_scheduler scheduler)
rv->bridge.suspend = ui_bridge_suspend;
rv->bridge.set_title = ui_bridge_set_title;
rv->bridge.set_icon = ui_bridge_set_icon;
rv->bridge.option_set = ui_bridge_option_set;
rv->scheduler = scheduler;
for (UIWidget i = 0; (int)i < UI_WIDGETS; i++) {
@ -144,6 +145,29 @@ static void ui_bridge_highlight_set_event(void **argv)
xfree(argv[1]);
}
static void ui_bridge_option_set(UI *ui, String name, Object value)
{
// Assumes bridge is only used by TUI
if (strequal(name.data, "termguicolors")) {
ui->rgb = value.data.boolean;
}
String copy_name = copy_string(name);
Object *copy_value = xmalloc(sizeof(Object));
*copy_value = copy_object(value);
UI_BRIDGE_CALL(ui, option_set, 4, ui,
copy_name.data, INT2PTR(copy_name.size), copy_value);
}
static void ui_bridge_option_set_event(void **argv)
{
UI *ui = UI(argv[0]);
String name = (String){ .data = argv[1], .size = (size_t)argv[2] };
Object value = *(Object *)argv[3];
ui->option_set(ui, name, value);
api_free_string(name);
api_free_object(value);
xfree(argv[3]);
}
static void ui_bridge_suspend(UI *b)
{
UIBridgeData *data = (UIBridgeData *)b;

View File

@ -2096,8 +2096,8 @@ void undo_time(long step, int sec, int file, int absolute)
uhp = uhp->uh_prev.ptr;
if (uhp == NULL || uhp->uh_walk != mark) {
/* Need to redo more but can't find it... */
EMSG2(_(e_intern2), "undo_time()");
// Need to redo more but can't find it...
internal_error("undo_time()");
break;
}
}
@ -2163,8 +2163,8 @@ static void u_undoredo(int undo)
if (top > curbuf->b_ml.ml_line_count || top >= bot
|| bot > curbuf->b_ml.ml_line_count + 1) {
unblock_autocmds();
EMSG(_("E438: u_undo: line numbers wrong"));
changed(); /* don't want UNCHANGED now */
IEMSG(_("E438: u_undo: line numbers wrong"));
changed(); // don't want UNCHANGED now
return;
}
@ -2655,7 +2655,7 @@ static void u_unch_branch(u_header_T *uhp)
static u_entry_T *u_get_headentry(void)
{
if (curbuf->b_u_newhead == NULL || curbuf->b_u_newhead->uh_entry == NULL) {
EMSG(_("E439: undo list corrupt"));
IEMSG(_("E439: undo list corrupt"));
return NULL;
}
return curbuf->b_u_newhead->uh_entry;
@ -2684,11 +2684,11 @@ static void u_getbot(void)
extra = curbuf->b_ml.ml_line_count - uep->ue_lcount;
uep->ue_bot = uep->ue_top + uep->ue_size + 1 + extra;
if (uep->ue_bot < 1 || uep->ue_bot > curbuf->b_ml.ml_line_count) {
EMSG(_("E440: undo line missing"));
uep->ue_bot = uep->ue_top + 1; /* assume all lines deleted, will
* get all the old lines back
* without deleting the current
* ones */
IEMSG(_("E440: undo line missing"));
uep->ue_bot = uep->ue_top + 1; // assume all lines deleted, will
// get all the old lines back
// without deleting the current
// ones
}
curbuf->b_u_newhead->uh_getbot_entry = NULL;

View File

@ -1182,7 +1182,7 @@ static const int included_patches[] = {
// 77 NA
// 76 NA
75,
// 74,
74,
73,
// 72 NA
// 71 NA

View File

@ -2115,7 +2115,7 @@ void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp)
ptp = ptp->tp_next)
;
if (ptp == NULL) {
EMSG2(_(e_intern2), "win_close_othertab()");
internal_error("win_close_othertab()");
return;
}
ptp->tp_next = tp->tp_next;

View File

@ -4,6 +4,7 @@ local global_helpers = require('test.helpers')
local uname = global_helpers.uname
local helpers = require('test.functional.helpers')(after_each)
local thelpers = require('test.functional.terminal.helpers')
local Screen = require('test.functional.ui.screen')
local eq = helpers.eq
local feed_data = thelpers.feed_data
local feed_command = helpers.feed_command
@ -179,6 +180,58 @@ describe('tui', function()
{3:-- TERMINAL --} |
]])
end)
it('allows termguicolors to be set at runtime', function()
screen:set_option('rgb', true)
screen:set_default_attr_ids({
[1] = {reverse = true},
[2] = {foreground = 13, special = Screen.colors.Grey0},
[3] = {special = Screen.colors.Grey0, bold = true, reverse = true},
[4] = {bold = true},
[5] = {special = Screen.colors.Grey0, reverse = true, foreground = 4},
[6] = {foreground = 4, special = Screen.colors.Grey0},
[7] = {special = Screen.colors.Grey0, reverse = true, foreground = Screen.colors.SeaGreen4},
[8] = {foreground = Screen.colors.SeaGreen4, special = Screen.colors.Grey0},
[9] = {special = Screen.colors.Grey0, bold = true, foreground = Screen.colors.Blue1},
})
feed_data(':hi SpecialKey ctermfg=3 guifg=SeaGreen\n')
feed_data('i')
feed_data('\022\007') -- ctrl+g
feed_data('\028\014') -- crtl+\ ctrl+N
feed_data(':set termguicolors?\n')
screen:expect([[
{5:^}{6:G} |
{2:~ }|
{2:~ }|
{2:~ }|
{3:[No Name] [+] }|
notermguicolors |
{4:-- TERMINAL --} |
]])
feed_data(':set termguicolors\n')
screen:expect([[
{7:^}{8:G} |
{9:~ }|
{9:~ }|
{9:~ }|
{3:[No Name] [+] }|
|
{4:-- TERMINAL --} |
]])
feed_data(':set notermguicolors\n')
screen:expect([[
{5:^}{6:G} |
{2:~ }|
{2:~ }|
{2:~ }|
{3:[No Name] [+] }|
|
{4:-- TERMINAL --} |
]])
end)
end)
describe('tui with non-tty file descriptors', function()

View File

@ -0,0 +1,66 @@
local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
local clear = helpers.clear
local command = helpers.command
local eq = helpers.eq
describe('ui receives option updates', function()
local screen
before_each(function()
clear()
screen = Screen.new(20,5)
screen:attach()
end)
after_each(function()
screen:detach()
end)
local defaults = {
ambiwidth='single',
arabicshape=true,
emoji=true,
guifont='',
guifontset='',
guifontwide='',
showtabline=1,
termguicolors=false,
}
it("for defaults", function()
screen:expect(function()
eq(defaults, screen.options)
end)
end)
it("when setting options", function()
local changed = {}
for k,v in pairs(defaults) do
changed[k] = v
end
command("set termguicolors")
changed.termguicolors = true
screen:expect(function()
eq(changed, screen.options)
end)
command("set guifont=Comic\\ Sans")
changed.guifont = "Comic Sans"
screen:expect(function()
eq(changed, screen.options)
end)
command("set showtabline=0")
changed.showtabline = 0
screen:expect(function()
eq(changed, screen.options)
end)
command("set all&")
screen:expect(function()
eq(defaults, screen.options)
end)
end)
end)

View File

@ -137,6 +137,7 @@ function Screen.new(width, height)
visual_bell = false,
suspended = false,
mode = 'normal',
options = {},
_default_attr_ids = nil,
_default_attr_ignore = nil,
_mouse_enabled = true,
@ -482,6 +483,10 @@ function Screen:_handle_set_icon(icon)
self.icon = icon
end
function Screen:_handle_option_set(name, value)
self.options[name] = value
end
function Screen:_clear_block(top, bot, left, right)
for i = top, bot do
self:_clear_row_section(i, left, right)