aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rwxr-xr-xsrc/clint.py24
-rw-r--r--src/nvim/api/ui.c29
-rw-r--r--src/nvim/api/ui_events.in.h2
-rw-r--r--src/nvim/buffer.c7
-rw-r--r--src/nvim/eval.c18
-rw-r--r--src/nvim/eval.lua2
-rw-r--r--src/nvim/ex_cmds.c18
-rw-r--r--src/nvim/ex_cmds2.c1
-rw-r--r--src/nvim/ex_docmd.c26
-rw-r--r--src/nvim/ex_getln.c14
-rw-r--r--src/nvim/globals.h6
-rw-r--r--src/nvim/log.c6
-rw-r--r--src/nvim/main.c19
-rw-r--r--src/nvim/memline.c1
-rw-r--r--src/nvim/move.c5
-rw-r--r--src/nvim/normal.c3
-rw-r--r--src/nvim/option.c33
-rw-r--r--src/nvim/os_unix.c3
-rw-r--r--src/nvim/screen.c14
-rw-r--r--src/nvim/syntax.c2
-rw-r--r--src/nvim/terminal.c1
-rw-r--r--src/nvim/testdir/test_alot.vim1
-rw-r--r--src/nvim/testdir/test_expr.vim3
-rw-r--r--src/nvim/testdir/test_getcwd.vim12
-rw-r--r--src/nvim/testdir/test_listdict.vim48
-rw-r--r--src/nvim/testdir/test_sort.vim85
-rw-r--r--src/nvim/testdir/test_suspend.vim51
-rw-r--r--src/nvim/testdir/test_unlet.vim34
-rw-r--r--src/nvim/tui/tui.c150
-rw-r--r--src/nvim/ugrid.c59
-rw-r--r--src/nvim/ugrid.h4
-rw-r--r--src/nvim/ui.h4
-rw-r--r--src/nvim/undo.c2
33 files changed, 518 insertions, 169 deletions
diff --git a/src/clint.py b/src/clint.py
index 9fd93ce143..34af5d15fd 100755
--- a/src/clint.py
+++ b/src/clint.py
@@ -49,6 +49,7 @@ from __future__ import unicode_literals
import codecs
import copy
+import fileinput
import getopt
import math # for log
import os
@@ -65,7 +66,7 @@ _USAGE = """
Syntax: clint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...]
[--counting=total|toplevel|detailed] [--root=subdir]
[--linelength=digits] [--record-errors=file]
- [--suppress-errors=file]
+ [--suppress-errors=file] [--stdin-filename=filename]
<file> [file] ...
The style guidelines this tries to follow are those in
@@ -167,6 +168,9 @@ Syntax: clint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...]
suppress-errors=file
Errors listed in the given file will not be reported.
+
+ stdin-filename=filename
+ Use specified filename when reading from stdin (file "-").
"""
# We categorize each error message we print. Here are the categories.
@@ -3456,10 +3460,12 @@ def ProcessFile(filename, vlevel, extra_check_functions=[]):
# is processed.
if filename == '-':
- lines = codecs.StreamReaderWriter(sys.stdin,
- codecs.getreader('utf8'),
- codecs.getwriter('utf8'),
- 'replace').read().split('\n')
+ stdin = sys.stdin.read()
+ if sys.version_info < (3, 0):
+ stdin = stdin.decode('utf8')
+ lines = stdin.split('\n')
+ if _cpplint_state.stdin_filename is not None:
+ filename = _cpplint_state.stdin_filename
else:
lines = codecs.open(
filename, 'r', 'utf8', 'replace').read().split('\n')
@@ -3540,7 +3546,9 @@ def ParseArguments(args):
'linelength=',
'extensions=',
'record-errors=',
- 'suppress-errors='])
+ 'suppress-errors=',
+ 'stdin-filename=',
+ ])
except getopt.GetoptError:
PrintUsage('Invalid arguments.')
@@ -3550,6 +3558,7 @@ def ParseArguments(args):
counting_style = ''
record_errors_file = None
suppress_errors_file = None
+ stdin_filename = None
for (opt, val) in opts:
if opt == '--help':
@@ -3586,6 +3595,8 @@ def ParseArguments(args):
record_errors_file = val
elif opt == '--suppress-errors':
suppress_errors_file = val
+ elif opt == '--stdin-filename':
+ stdin_filename = val
if not filenames:
PrintUsage('No files were specified.')
@@ -3596,6 +3607,7 @@ def ParseArguments(args):
_SetCountingStyle(counting_style)
_SuppressErrorsFrom(suppress_errors_file)
_RecordErrorsTo(record_errors_file)
+ _cpplint_state.stdin_filename = stdin_filename
return filenames
diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c
index b49de7abf3..01f8c9f71c 100644
--- a/src/nvim/api/ui.c
+++ b/src/nvim/api/ui.c
@@ -127,7 +127,7 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height,
}
if (ui->ui_ext[kUIHlState]) {
- ui->ui_ext[kUINewgrid] = true;
+ ui->ui_ext[kUILinegrid] = true;
}
UIData *data = xmalloc(sizeof(UIData));
@@ -227,11 +227,11 @@ static void ui_set_option(UI *ui, bool init, String name, Object value,
return;
}
bool boolval = value.data.boolean;
- if (!init && i == kUINewgrid && boolval != ui->ui_ext[i]) {
+ if (!init && i == kUILinegrid && boolval != ui->ui_ext[i]) {
// There shouldn't be a reason for an UI to do this ever
// so explicitly don't support this.
api_set_error(error, kErrorTypeValidation,
- "ext_newgrid option cannot be changed");
+ "ext_linegrid option cannot be changed");
}
ui->ui_ext[i] = boolval;
if (!init) {
@@ -271,10 +271,10 @@ static void push_call(UI *ui, const char *name, Array args)
static void remote_ui_grid_clear(UI *ui, Integer grid)
{
Array args = ARRAY_DICT_INIT;
- if (ui->ui_ext[kUINewgrid]) {
+ if (ui->ui_ext[kUILinegrid]) {
ADD(args, INTEGER_OBJ(grid));
}
- const char *name = ui->ui_ext[kUINewgrid] ? "grid_clear" : "clear";
+ const char *name = ui->ui_ext[kUILinegrid] ? "grid_clear" : "clear";
push_call(ui, name, args);
}
@@ -282,12 +282,12 @@ static void remote_ui_grid_resize(UI *ui, Integer grid,
Integer width, Integer height)
{
Array args = ARRAY_DICT_INIT;
- if (ui->ui_ext[kUINewgrid]) {
+ if (ui->ui_ext[kUILinegrid]) {
ADD(args, INTEGER_OBJ(grid));
}
ADD(args, INTEGER_OBJ(width));
ADD(args, INTEGER_OBJ(height));
- const char *name = ui->ui_ext[kUINewgrid] ? "grid_resize" : "resize";
+ const char *name = ui->ui_ext[kUILinegrid] ? "grid_resize" : "resize";
push_call(ui, name, args);
}
@@ -295,7 +295,7 @@ static void remote_ui_grid_scroll(UI *ui, Integer grid, Integer top,
Integer bot, Integer left, Integer right,
Integer rows, Integer cols)
{
- if (ui->ui_ext[kUINewgrid]) {
+ if (ui->ui_ext[kUILinegrid]) {
Array args = ARRAY_DICT_INIT;
ADD(args, INTEGER_OBJ(grid));
ADD(args, INTEGER_OBJ(top));
@@ -341,7 +341,7 @@ static void remote_ui_default_colors_set(UI *ui, Integer rgb_fg,
push_call(ui, "default_colors_set", args);
// Deprecated
- if (!ui->ui_ext[kUINewgrid]) {
+ if (!ui->ui_ext[kUILinegrid]) {
args = (Array)ARRAY_DICT_INIT;
ADD(args, INTEGER_OBJ(ui->rgb ? rgb_fg : cterm_fg - 1));
push_call(ui, "update_fg", args);
@@ -359,7 +359,7 @@ static void remote_ui_default_colors_set(UI *ui, Integer rgb_fg,
static void remote_ui_hl_attr_define(UI *ui, Integer id, HlAttrs rgb_attrs,
HlAttrs cterm_attrs, Array info)
{
- if (!ui->ui_ext[kUINewgrid]) {
+ if (!ui->ui_ext[kUILinegrid]) {
return;
}
Array args = ARRAY_DICT_INIT;
@@ -397,7 +397,7 @@ static void remote_ui_highlight_set(UI *ui, int id)
static void remote_ui_grid_cursor_goto(UI *ui, Integer grid, Integer row,
Integer col)
{
- if (ui->ui_ext[kUINewgrid]) {
+ if (ui->ui_ext[kUILinegrid]) {
Array args = ARRAY_DICT_INIT;
ADD(args, INTEGER_OBJ(grid));
ADD(args, INTEGER_OBJ(row));
@@ -442,7 +442,7 @@ static void remote_ui_raw_line(UI *ui, Integer grid, Integer row,
const sattr_T *attrs)
{
UIData *data = ui->data;
- if (ui->ui_ext[kUINewgrid]) {
+ if (ui->ui_ext[kUILinegrid]) {
Array args = ARRAY_DICT_INIT;
ADD(args, INTEGER_OBJ(grid));
ADD(args, INTEGER_OBJ(row));
@@ -508,9 +508,10 @@ static void remote_ui_flush(UI *ui)
{
UIData *data = ui->data;
if (data->buffer.size > 0) {
- if (!ui->ui_ext[kUINewgrid]) {
+ if (!ui->ui_ext[kUILinegrid]) {
remote_ui_cursor_goto(ui, data->cursor_row, data->cursor_col);
}
+ push_call(ui, "flush", (Array)ARRAY_DICT_INIT);
rpc_send_event(data->channel_id, "redraw", data->buffer);
data->buffer = (Array)ARRAY_DICT_INIT;
}
@@ -549,7 +550,7 @@ static Array translate_firstarg(UI *ui, Array args)
static void remote_ui_event(UI *ui, char *name, Array args, bool *args_consumed)
{
- if (!ui->ui_ext[kUINewgrid]) {
+ if (!ui->ui_ext[kUILinegrid]) {
// the representation of highlights in cmdline changed, translate back
// never consumes args
if (strequal(name, "cmdline_show")) {
diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h
index 456ad0c8cc..9ddf788376 100644
--- a/src/nvim/api/ui_events.in.h
+++ b/src/nvim/api/ui_events.in.h
@@ -63,7 +63,7 @@ void set_scroll_region(Integer top, Integer bot, Integer left, Integer right)
void scroll(Integer count)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
-// Second revison of the grid protocol, used with ext_newgrid ui option
+// Second revison of the grid protocol, used with ext_linegrid ui option
void default_colors_set(Integer rgb_fg, Integer rgb_bg, Integer rgb_sp,
Integer cterm_fg, Integer cterm_bg)
FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL;
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index a28d9774ab..68f6ff303b 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -4533,9 +4533,10 @@ do_arg_all (
use_firstwin = true;
}
- for (i = 0; i < count && i < opened_len && !got_int; ++i) {
- if (alist == &global_alist && i == global_alist.al_ga.ga_len - 1)
- arg_had_last = TRUE;
+ for (i = 0; i < count && i < opened_len && !got_int; i++) {
+ if (alist == &global_alist && i == global_alist.al_ga.ga_len - 1) {
+ arg_had_last = true;
+ }
if (opened[i] > 0) {
/* Move the already present window to below the current window */
if (curwin->w_arg_idx != i) {
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 2c12534b21..9c678168bb 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -2818,6 +2818,18 @@ static void ex_unletlock(exarg_T *eap, char_u *argstart, int deep)
lval_T lv;
do {
+ if (*arg == '$') {
+ const char *name = (char *)++arg;
+
+ if (get_env_len((const char_u **)&arg) == 0) {
+ EMSG2(_(e_invarg2), name - 1);
+ return;
+ }
+ os_unsetenv(name);
+ arg = skipwhite(arg);
+ continue;
+ }
+
// Parse the name and find the end.
char_u *const name_end = (char_u *)get_lval(arg, NULL, &lv, true,
eap->skip || error,
@@ -14664,6 +14676,7 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv)
static char *e_invact = N_("E927: Invalid action: '%s'");
const char *title = NULL;
int action = ' ';
+ static int recursive = 0;
rettv->vval.v_number = -1;
dict_T *d = NULL;
@@ -14671,6 +14684,9 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv)
if (list_arg->v_type != VAR_LIST) {
EMSG(_(e_listreq));
return;
+ } else if (recursive != 0) {
+ EMSG(_(e_au_recursive));
+ return;
}
typval_T *action_arg = &args[1];
@@ -14712,10 +14728,12 @@ skip_args:
title = (wp ? "setloclist()" : "setqflist()");
}
+ recursive++;
list_T *const l = list_arg->vval.v_list;
if (set_errorlist(wp, l, action, (char_u *)title, d) == OK) {
rettv->vval.v_number = 0;
}
+ recursive--;
}
/*
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index 328f46443f..e72bb7b870 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -222,7 +222,7 @@ return {
pathshorten={args=1},
pow={args=2},
prevnonblank={args=1},
- printf={args=varargs(2)},
+ printf={args=varargs(1)},
pumvisible={},
py3eval={args=1},
pyeval={args=1},
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index aaa4dbdfb7..a091862e42 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -424,6 +424,7 @@ void ex_sort(exarg_T *eap)
sort_abort = sort_ic = sort_rx = sort_nr = sort_flt = 0;
size_t format_found = 0;
+ bool change_occurred = false; // Buffer contents changed.
for (p = eap->arg; *p != NUL; ++p) {
if (ascii_iswhite(*p)) {
@@ -584,8 +585,16 @@ void ex_sort(exarg_T *eap)
// Insert the lines in the sorted order below the last one.
lnum = eap->line2;
- for (i = 0; i < count; ++i) {
- s = ml_get(nrs[eap->forceit ? count - i - 1 : i].lnum);
+ for (i = 0; i < count; i++) {
+ const linenr_T get_lnum = nrs[eap->forceit ? count - i - 1 : i].lnum;
+
+ // If the original line number of the line being placed is not the same
+ // as "lnum" (accounting for offset), we know that the buffer changed.
+ if (get_lnum + ((linenr_T)count - 1) != lnum) {
+ change_occurred = true;
+ }
+
+ s = ml_get(get_lnum);
if (!unique || i == 0
|| (sort_ic ? STRICMP(s, sortbuf1) : STRCMP(s, sortbuf1)) != 0) {
// Copy the line into a buffer, it may become invalid in
@@ -614,10 +623,13 @@ void ex_sort(exarg_T *eap)
if (deleted > 0) {
mark_adjust(eap->line2 - deleted, eap->line2, (long)MAXLNUM, -deleted,
false);
+ msgmore(-deleted);
} else if (deleted < 0) {
mark_adjust(eap->line2, MAXLNUM, -deleted, 0L, false);
}
- changed_lines(eap->line1, 0, eap->line2 + 1, -deleted, true);
+ if (change_occurred || deleted != 0) {
+ changed_lines(eap->line1, 0, eap->line2 + 1, -deleted, true);
+ }
curwin->w_cursor.lnum = eap->line1;
beginline(BL_WHITE | BL_FIX);
diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c
index 6e695a8897..90fb7b8bc3 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -1285,7 +1285,6 @@ void dialog_changed(buf_T *buf, bool checkall)
int ret;
exarg_T ea;
- assert(buf->b_fname != NULL);
dialog_msg(buff, _("Save changes to \"%s\"?"), buf->b_fname);
if (checkall) {
ret = vim_dialog_yesnoallcancel(VIM_QUESTION, NULL, buff, 1);
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index ad51de46ee..2a733f5831 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -3260,8 +3260,15 @@ const char * set_one_cmd_context(
while ((xp->xp_pattern = (char_u *)strchr(arg, ' ')) != NULL) {
arg = (const char *)xp->xp_pattern + 1;
}
+
xp->xp_context = EXPAND_USER_VARS;
xp->xp_pattern = (char_u *)arg;
+
+ if (*xp->xp_pattern == '$') {
+ xp->xp_context = EXPAND_ENV_VARS;
+ xp->xp_pattern++;
+ }
+
break;
case CMD_function:
@@ -6536,6 +6543,13 @@ void alist_expand(int *fnum_list, int fnum_len)
void alist_set(alist_T *al, int count, char_u **files, int use_curbuf, int *fnum_list, int fnum_len)
{
int i;
+ static int recursive = 0;
+
+ if (recursive) {
+ EMSG(_(e_au_recursive));
+ return;
+ }
+ recursive++;
alist_clear(al);
ga_grow(&al->al_ga, count);
@@ -6560,8 +6574,10 @@ void alist_set(alist_T *al, int count, char_u **files, int use_curbuf, int *fnum
xfree(files);
}
- if (al == &global_alist)
- arg_had_last = FALSE;
+ if (al == &global_alist) {
+ arg_had_last = false;
+ }
+ recursive--;
}
/*
@@ -9040,8 +9056,10 @@ makeopens(
// cursor can be set. This is done again below.
// winminheight and winminwidth need to be set to avoid an error if the
// user has set winheight or winwidth.
- if (put_line(fd, "set winminheight=1 winminwidth=1 winheight=1 winwidth=1")
- == FAIL) {
+ if (put_line(fd, "set winminheight=0") == FAIL
+ || put_line(fd, "set winheight=1") == FAIL
+ || put_line(fd, "set winminwidth=0") == FAIL
+ || put_line(fd, "set winwidth=1") == FAIL) {
return FAIL;
}
if (nr > 1 && ses_winsizes(fd, restore_size, tab_firstwin) == FAIL) {
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index 4983484100..1ec00b1e25 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -793,9 +793,11 @@ static int command_line_execute(VimState *state, int key)
no_mapping--;
// CTRL-\ e doesn't work when obtaining an expression, unless it
// is in a mapping.
- if (s->c != Ctrl_N && s->c != Ctrl_G && (s->c != 'e'
- || (ccline.cmdfirstc == '='
- && KeyTyped))) {
+ if (s->c != Ctrl_N
+ && s->c != Ctrl_G
+ && (s->c != 'e'
+ || (ccline.cmdfirstc == '=' && KeyTyped)
+ || cmdline_star > 0)) {
vungetc(s->c);
s->c = Ctrl_BSL;
} else if (s->c == 'e') {
@@ -1350,7 +1352,8 @@ static int command_line_handle_key(CommandLineState *s)
// a new one...
new_cmdpos = -1;
if (s->c == '=') {
- if (ccline.cmdfirstc == '=') { // can't do this recursively
+ if (ccline.cmdfirstc == '=' // can't do this recursively
+ || cmdline_star > 0) { // or when typing a password
beep_flush();
s->c = ESC;
} else {
@@ -5587,6 +5590,9 @@ static struct cmdline_info *get_ccline_ptr(void)
*/
char_u *get_cmdline_str(void)
{
+ if (cmdline_star > 0) {
+ return NULL;
+ }
struct cmdline_info *p = get_ccline_ptr();
if (p == NULL)
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 9bba2379cd..2dc8073b1e 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -528,8 +528,8 @@ EXTERN buf_T *curbuf INIT(= NULL); // currently active buffer
*/
EXTERN alist_T global_alist; /* global argument list */
EXTERN int max_alist_id INIT(= 0); ///< the previous argument list id
-EXTERN int arg_had_last INIT(= FALSE); /* accessed last file in
- global_alist */
+EXTERN bool arg_had_last INIT(= false); // accessed last file in
+ // global_alist
EXTERN int ru_col; /* column for ruler */
EXTERN int ru_wid; /* 'rulerfmt' width of ruler when non-zero */
@@ -1099,6 +1099,8 @@ EXTERN char_u e_notset[] INIT(= N_("E764: Option '%s' is not set"));
EXTERN char_u e_invalidreg[] INIT(= N_("E850: Invalid register name"));
EXTERN char_u e_dirnotf[] INIT(= N_(
"E919: Directory not found in '%s': \"%s\""));
+EXTERN char_u e_au_recursive[] INIT(= N_(
+ "E952: Autocommand caused recursive behavior"));
EXTERN char_u e_unsupportedoption[] INIT(= N_("E519: Option not supported"));
EXTERN char_u e_fnametoolong[] INIT(= N_("E856: Filename too long"));
EXTERN char_u e_float_as_string[] INIT(= N_("E806: using Float as a String"));
diff --git a/src/nvim/log.c b/src/nvim/log.c
index 503c4b122c..4d912c452b 100644
--- a/src/nvim/log.c
+++ b/src/nvim/log.c
@@ -109,6 +109,12 @@ bool logmsg(int log_level, const char *context, const char *func_name,
return false;
}
+#ifdef EXITFREE
+ // Logging after we've already started freeing all our memory will only cause
+ // pain. We need access to VV_PROGPATH, homedir, etc.
+ assert(!entered_free_all_mem);
+#endif
+
log_lock();
bool ret = false;
FILE *log_file = open_log_file();
diff --git a/src/nvim/main.c b/src/nvim/main.c
index 41e8c42803..af54e62393 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -258,6 +258,14 @@ int main(int argc, char **argv)
// Process the command line arguments. File names are put in the global
// argument list "global_alist".
command_line_scan(&params);
+
+ if (embedded_mode) {
+ const char *err;
+ if (!channel_from_stdio(true, CALLBACK_READER_INIT, &err)) {
+ abort();
+ }
+ }
+
server_init(params.listen_addr);
if (GARGCOUNT > 0) {
@@ -848,10 +856,6 @@ static void command_line_scan(mparm_T *parmp)
headless_mode = true;
} else if (STRICMP(argv[0] + argv_idx, "embed") == 0) {
embedded_mode = true;
- const char *err;
- if (!channel_from_stdio(true, CALLBACK_READER_INIT, &err)) {
- abort();
- }
} else if (STRNICMP(argv[0] + argv_idx, "listen", 6) == 0) {
want_argument = true;
argv_idx += 6;
@@ -1624,9 +1628,10 @@ static void edit_buffers(mparm_T *parmp, char_u *cwd)
win_close(curwin, true);
advance = false;
}
- if (arg_idx == GARGCOUNT - 1)
- arg_had_last = TRUE;
- ++arg_idx;
+ if (arg_idx == GARGCOUNT - 1) {
+ arg_had_last = true;
+ }
+ arg_idx++;
}
os_breakcheck();
if (got_int) {
diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index 5602a29f50..ec0238e7c9 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -535,6 +535,7 @@ void ml_open_file(buf_T *buf)
void check_need_swap(int newfile)
{
int old_msg_silent = msg_silent; // might be reset by an E325 message
+ msg_silent = 0; // If swap dialog prompts for input, user needs to see it!
if (curbuf->b_may_swap && (!curbuf->b_p_ro || !newfile)) {
ml_open_file(curbuf);
diff --git a/src/nvim/move.c b/src/nvim/move.c
index 4a2874abeb..442e5d6dff 100644
--- a/src/nvim/move.c
+++ b/src/nvim/move.c
@@ -98,6 +98,11 @@ static void comp_botline(win_T *wp)
static linenr_T last_cursorline = 0;
+void reset_cursorline(void)
+{
+ last_cursorline = 0;
+}
+
// Redraw when w_cline_row changes and 'relativenumber' or 'cursorline' is set.
static void redraw_for_cursorline(win_T *wp)
{
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 45620bfc54..f87de52a82 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -1269,7 +1269,8 @@ static void normal_redraw(NormalState *s)
xfree(p);
}
- if (need_fileinfo) { // show file info after redraw
+ // show fileinfo after redraw
+ if (need_fileinfo && !shortmess(SHM_FILEINFO)) {
fileinfo(false, true, false);
need_fileinfo = false;
}
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 7cda42ef20..eb2780ce7a 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -2434,7 +2434,7 @@ did_set_string_option (
int did_chartab = FALSE;
char_u **gvarp;
bool free_oldval = (options[opt_idx].flags & P_ALLOCED);
- int ft_changed = false;
+ bool value_changed = false;
/* Get the global option to compare with, otherwise we would have to check
* two values for all local options. */
@@ -3155,11 +3155,13 @@ did_set_string_option (
if (!valid_filetype(*varp)) {
errmsg = e_invarg;
} else {
- ft_changed = STRCMP(oldval, *varp) != 0;
+ value_changed = STRCMP(oldval, *varp) != 0;
}
} else if (gvarp == &p_syn) {
if (!valid_filetype(*varp)) {
errmsg = e_invarg;
+ } else {
+ value_changed = STRCMP(oldval, *varp) != 0;
}
} else if (varp == &curwin->w_p_winhl) {
if (!parse_winhl_opt(curwin)) {
@@ -3235,14 +3237,28 @@ did_set_string_option (
*/
/* When 'syntax' is set, load the syntax of that name */
if (varp == &(curbuf->b_p_syn)) {
- apply_autocmds(EVENT_SYNTAX, curbuf->b_p_syn,
- curbuf->b_fname, TRUE, curbuf);
+ static int syn_recursive = 0;
+
+ syn_recursive++;
+ // Only pass true for "force" when the value changed or not used
+ // recursively, to avoid endless recurrence.
+ apply_autocmds(EVENT_SYNTAX, curbuf->b_p_syn, curbuf->b_fname,
+ value_changed || syn_recursive == 1, curbuf);
+ syn_recursive--;
} else if (varp == &(curbuf->b_p_ft)) {
// 'filetype' is set, trigger the FileType autocommand
- if (!(opt_flags & OPT_MODELINE) || ft_changed) {
+ // Skip this when called from a modeline and the filetype was
+ // already set to this value.
+ if (!(opt_flags & OPT_MODELINE) || value_changed) {
+ static int ft_recursive = 0;
+
+ ft_recursive++;
did_filetype = true;
- apply_autocmds(EVENT_FILETYPE, curbuf->b_p_ft,
- curbuf->b_fname, true, curbuf);
+ // Only pass true for "force" when the value changed or not
+ // used recursively, to avoid endless recurrence.
+ apply_autocmds(EVENT_FILETYPE, curbuf->b_p_ft, curbuf->b_fname,
+ value_changed || ft_recursive == 1, curbuf);
+ ft_recursive--;
// Just in case the old "curbuf" is now invalid
if (varp != &(curbuf->b_p_ft)) {
varp = NULL;
@@ -3705,6 +3721,9 @@ static char *set_bool_option(const int opt_idx, char_u *const varp,
} else if ((int *)varp == &p_lnr) {
// 'langnoremap' -> !'langremap'
p_lrm = !p_lnr;
+ } else if ((int *)varp == &curwin->w_p_cul && !value && old_value) {
+ // 'cursorline'
+ reset_cursorline();
// 'undofile'
} else if ((int *)varp == &curbuf->b_p_udf || (int *)varp == &p_udf) {
// Only take action when the option was set. When reset we do not
diff --git a/src/nvim/os_unix.c b/src/nvim/os_unix.c
index 27660712da..09ba718302 100644
--- a/src/nvim/os_unix.c
+++ b/src/nvim/os_unix.c
@@ -149,11 +149,12 @@ void mch_exit(int r)
stream_set_blocking(input_global_fd(), true); // normalize stream (#2598)
}
+ ILOG("Nvim exit: %d", r);
+
#ifdef EXITFREE
free_all_mem();
#endif
- ILOG("Nvim exit: %d", r);
exit(r);
}
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index fe6a15c5fc..ec4b31c40d 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -4588,14 +4588,18 @@ void status_redraw_all(void)
}
}
-/*
- * mark all status lines of the current buffer for redraw
- */
+/// Marks all status lines of the current buffer for redraw.
void status_redraw_curbuf(void)
{
+ status_redraw_buf(curbuf);
+}
+
+/// Marks all status lines of the specified buffer for redraw.
+void status_redraw_buf(buf_T *buf)
+{
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_status_height != 0 && wp->w_buffer == curbuf) {
- wp->w_redr_status = TRUE;
+ if (wp->w_status_height != 0 && wp->w_buffer == buf) {
+ wp->w_redr_status = true;
redraw_later(VALID);
}
}
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index 4c054dc8e0..e0e1897b88 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -6884,7 +6884,7 @@ void do_highlight(const char *line, const bool forceit, const bool init)
// "fg", which have been changed now.
highlight_attr_set_all();
- if (!ui_is_external(kUINewgrid) && starting == 0) {
+ if (!ui_is_external(kUILinegrid) && starting == 0) {
// Older UIs assume that we clear the screen after normal group is
// changed
ui_refresh();
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c
index d83986cb15..d831979022 100644
--- a/src/nvim/terminal.c
+++ b/src/nvim/terminal.c
@@ -668,6 +668,7 @@ static void buf_set_term_title(buf_T *buf, char *title)
false,
&err);
api_clear_error(&err);
+ status_redraw_buf(buf);
}
static int term_settermprop(VTermProp prop, VTermValue *val, void *data)
diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim
index f10163e351..36dcdc3386 100644
--- a/src/nvim/testdir/test_alot.vim
+++ b/src/nvim/testdir/test_alot.vim
@@ -38,6 +38,7 @@ source test_sort.vim
source test_source_utf8.vim
source test_sha256.vim
source test_statusline.vim
+source test_suspend.vim
source test_syn_attr.vim
source test_tabline.vim
" source test_tabpage.vim
diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim
index ad967c528c..aaf32dff04 100644
--- a/src/nvim/testdir/test_expr.vim
+++ b/src/nvim/testdir/test_expr.vim
@@ -166,6 +166,9 @@ function Test_printf_spec_s()
endfunc
function Test_printf_misc()
+ call assert_equal('123', printf('123'))
+ call assert_fails("call printf('123', 3)", "E767:")
+
call assert_equal('123', printf('%d', 123))
call assert_equal('123', printf('%i', 123))
call assert_equal('123', printf('%D', 123))
diff --git a/src/nvim/testdir/test_getcwd.vim b/src/nvim/testdir/test_getcwd.vim
index 15eab2abbb..194963f694 100644
--- a/src/nvim/testdir/test_getcwd.vim
+++ b/src/nvim/testdir/test_getcwd.vim
@@ -89,3 +89,15 @@ function Test_GetCwd()
call assert_equal("y Xdir2 1", GetCwdInfo(2, tp_nr))
call assert_equal("z Xdir3 1", GetCwdInfo(1, tp_nr))
endfunc
+
+function Test_GetCwd_lcd_shellslash()
+ new
+ let root = fnamemodify('/', ':p')
+ exe 'lcd '.root
+ let cwd = getcwd()
+ if !exists('+shellslash') || &shellslash
+ call assert_equal(cwd[-1:], '/')
+ else
+ call assert_equal(cwd[-1:], '\')
+ endif
+endfunc
diff --git a/src/nvim/testdir/test_listdict.vim b/src/nvim/testdir/test_listdict.vim
index 023332c90a..999d4dbd4a 100644
--- a/src/nvim/testdir/test_listdict.vim
+++ b/src/nvim/testdir/test_listdict.vim
@@ -106,6 +106,43 @@ func Test_list_range_assign()
call assert_equal([1, 2], l)
endfunc
+" Test removing items in list
+func Test_list_func_remove()
+ " Test removing 1 element
+ let l = [1, 2, 3, 4]
+ call assert_equal(1, remove(l, 0))
+ call assert_equal([2, 3, 4], l)
+
+ let l = [1, 2, 3, 4]
+ call assert_equal(2, remove(l, 1))
+ call assert_equal([1, 3, 4], l)
+
+ let l = [1, 2, 3, 4]
+ call assert_equal(4, remove(l, -1))
+ call assert_equal([1, 2, 3], l)
+
+ " Test removing range of element(s)
+ let l = [1, 2, 3, 4]
+ call assert_equal([3], remove(l, 2, 2))
+ call assert_equal([1, 2, 4], l)
+
+ let l = [1, 2, 3, 4]
+ call assert_equal([2, 3], remove(l, 1, 2))
+ call assert_equal([1, 4], l)
+
+ let l = [1, 2, 3, 4]
+ call assert_equal([2, 3], remove(l, -3, -2))
+ call assert_equal([1, 4], l)
+
+ " Test invalid cases
+ let l = [1, 2, 3, 4]
+ call assert_fails("call remove(l, 5)", 'E684:')
+ call assert_fails("call remove(l, 1, 5)", 'E684:')
+ call assert_fails("call remove(l, 3, 2)", 'E16:')
+ call assert_fails("call remove(1, 0)", 'E712:')
+ call assert_fails("call remove(l, l)", 'E745:')
+endfunc
+
" Tests for Dictionary type
func Test_dict()
@@ -222,6 +259,17 @@ func Test_script_local_dict_func()
unlet g:dict
endfunc
+" Test removing items in la dictionary
+func Test_dict_func_remove()
+ let d = {1:'a', 2:'b', 3:'c'}
+ call assert_equal('b', remove(d, 2))
+ call assert_equal({1:'a', 3:'c'}, d)
+
+ call assert_fails("call remove(d, 1, 2)", 'E118:')
+ call assert_fails("call remove(d, 'a')", 'E716:')
+ call assert_fails("call remove(d, [])", 'E730:')
+endfunc
+
" Nasty: remove func from Dict that's being called (works)
func Test_dict_func_remove_in_use()
let d = {1:1}
diff --git a/src/nvim/testdir/test_sort.vim b/src/nvim/testdir/test_sort.vim
index 4fddb47b58..14d008a17f 100644
--- a/src/nvim/testdir/test_sort.vim
+++ b/src/nvim/testdir/test_sort.vim
@@ -1,13 +1,13 @@
-" Test sort()
+" Tests for the "sort()" function and for the ":sort" command.
-:func Compare1(a, b) abort
+func Compare1(a, b) abort
call sort(range(3), 'Compare2')
return a:a - a:b
-:endfunc
+endfunc
-:func Compare2(a, b) abort
+func Compare2(a, b) abort
return a:a - a:b
-:endfunc
+endfunc
func Test_sort_strings()
" numbers compared as strings
@@ -45,7 +45,7 @@ func Test_sort_default()
call assert_fails('call sort([3.3, 1, "2"], 3)', "E474")
endfunc
-" Tests for the :sort command
+" Tests for the ":sort" command.
func Test_sort_cmd()
let tests = [
\ {
@@ -1167,18 +1167,87 @@ func Test_sort_cmd()
\ '1.234',
\ '123.456'
\ ]
- \ }
+ \ },
+ \ {
+ \ 'name' : 'alphabetical, sorted input',
+ \ 'cmd' : 'sort',
+ \ 'input' : [
+ \ 'a',
+ \ 'b',
+ \ 'c',
+ \ ],
+ \ 'expected' : [
+ \ 'a',
+ \ 'b',
+ \ 'c',
+ \ ]
+ \ },
+ \ {
+ \ 'name' : 'alphabetical, sorted input, unique at end',
+ \ 'cmd' : 'sort u',
+ \ 'input' : [
+ \ 'aa',
+ \ 'bb',
+ \ 'cc',
+ \ 'cc',
+ \ ],
+ \ 'expected' : [
+ \ 'aa',
+ \ 'bb',
+ \ 'cc',
+ \ ]
+ \ },
\ ]
for t in tests
enew!
call append(0, t.input)
$delete _
- exe t.cmd
+ setlocal nomodified
+ execute t.cmd
+
call assert_equal(t.expected, getline(1, '$'), t.name)
+
+ " Previously, the ":sort" command would set 'modified' even if the buffer
+ " contents did not change. Here, we check that this problem is fixed.
+ if t.input == t.expected
+ call assert_false(&modified, t.name . ': &mod is not correct')
+ else
+ call assert_true(&modified, t.name . ': &mod is not correct')
+ endif
endfor
call assert_fails('sort no', 'E474')
enew!
endfunc
+
+func Test_sort_cmd_report()
+ enew!
+ call append(0, repeat([1], 3) + repeat([2], 3) + repeat([3], 3))
+ $delete _
+ setlocal nomodified
+ let res = execute('%sort u')
+
+ call assert_equal([1,2,3], map(getline(1, '$'), 'v:val+0'))
+ call assert_match("6 fewer lines", res)
+ enew!
+ call append(0, repeat([1], 3) + repeat([2], 3) + repeat([3], 3))
+ $delete _
+ setlocal nomodified report=10
+ let res = execute('%sort u')
+
+ call assert_equal([1,2,3], map(getline(1, '$'), 'v:val+0'))
+ call assert_equal("", res)
+ enew!
+ call append(0, repeat([1], 3) + repeat([2], 3) + repeat([3], 3))
+ $delete _
+ setl report&vim
+ setlocal nomodified
+ let res = execute('1g/^/%sort u')
+
+ call assert_equal([1,2,3], map(getline(1, '$'), 'v:val+0'))
+ " the output comes from the :g command, not from the :sort
+ call assert_match("6 fewer lines", res)
+ enew!
+ endfunc
diff --git a/src/nvim/testdir/test_suspend.vim b/src/nvim/testdir/test_suspend.vim
new file mode 100644
index 0000000000..462173e8cc
--- /dev/null
+++ b/src/nvim/testdir/test_suspend.vim
@@ -0,0 +1,51 @@
+" Test :suspend
+
+source shared.vim
+
+func Test_suspend()
+ if !has('terminal') || !executable('/bin/sh')
+ return
+ endif
+
+ let buf = term_start('/bin/sh')
+ " Wait for shell prompt.
+ call WaitForAssert({-> assert_match('$ $', term_getline(buf, '.'))})
+
+ call term_sendkeys(buf, v:progpath
+ \ . " --clean -X"
+ \ . " -c 'set nu'"
+ \ . " -c 'call setline(1, \"foo\")'"
+ \ . " Xfoo\<CR>")
+ " Cursor in terminal buffer should be on first line in spawned vim.
+ call WaitForAssert({-> assert_equal(' 1 foo', term_getline(buf, '.'))})
+
+ for suspend_cmd in [":suspend\<CR>",
+ \ ":stop\<CR>",
+ \ ":suspend!\<CR>",
+ \ ":stop!\<CR>",
+ \ "\<C-Z>"]
+ " Suspend and wait for shell prompt.
+ call term_sendkeys(buf, suspend_cmd)
+ call WaitForAssert({-> assert_match('$ $', term_getline(buf, '.'))})
+
+ " Without 'autowrite', buffer should not be written.
+ call assert_equal(0, filereadable('Xfoo'))
+
+ call term_sendkeys(buf, "fg\<CR>")
+ call WaitForAssert({-> assert_equal(' 1 foo', term_getline(buf, '.'))})
+ endfor
+
+ " Test that :suspend! with 'autowrite' writes content of buffers if modified.
+ call term_sendkeys(buf, ":set autowrite\<CR>")
+ call assert_equal(0, filereadable('Xfoo'))
+ call term_sendkeys(buf, ":suspend\<CR>")
+ " Wait for shell prompt.
+ call WaitForAssert({-> assert_match('$ $', term_getline(buf, '.'))})
+ call assert_equal(['foo'], readfile('Xfoo'))
+ call term_sendkeys(buf, "fg\<CR>")
+ call WaitForAssert({-> assert_equal(' 1 foo', term_getline(buf, '.'))})
+
+ exe buf . 'bwipe!'
+ call delete('Xfoo')
+ set autowrite&
+endfunc
diff --git a/src/nvim/testdir/test_unlet.vim b/src/nvim/testdir/test_unlet.vim
index 3f06058d03..b02bdaab3b 100644
--- a/src/nvim/testdir/test_unlet.vim
+++ b/src/nvim/testdir/test_unlet.vim
@@ -28,3 +28,37 @@ endfunc
func Test_unlet_fails()
call assert_fails('unlet v:["count"]', 'E46:')
endfunc
+
+func Test_unlet_env()
+ let envcmd = has('win32') ? 'set' : 'env'
+
+ let $FOOBAR = 'test'
+ let found = 0
+ for kv in split(system(envcmd), "\r*\n")
+ if kv == 'FOOBAR=test'
+ let found = 1
+ endif
+ endfor
+ call assert_equal(1, found)
+
+ unlet $FOOBAR
+ let found = 0
+ for kv in split(system(envcmd), "\r*\n")
+ if kv == 'FOOBAR=test'
+ let found = 1
+ endif
+ endfor
+ call assert_equal(0, found)
+
+ unlet $MUST_NOT_BE_AN_ERROR
+endfunc
+
+func Test_unlet_complete()
+ let g:FOOBAR = 1
+ call feedkeys(":unlet g:FOO\t\n", 'tx')
+ call assert_true(!exists('g:FOOBAR'))
+
+ let $FOOBAR = 1
+ call feedkeys(":unlet $FOO\t\n", 'tx')
+ call assert_true(!exists('$FOOBAR') || empty($FOOBAR))
+endfunc
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index 713fe6a2e5..3ed0fe0cd6 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -118,6 +118,7 @@ typedef struct {
int resize_screen;
int reset_scroll_region;
int set_cursor_style, reset_cursor_style;
+ int enter_undercurl_mode, exit_undercurl_mode, set_underline_color;
} unibi_ext;
} TUIData;
@@ -156,7 +157,7 @@ UI *tui_start(void)
ui->raw_line = tui_raw_line;
memset(ui->ui_ext, 0, sizeof(ui->ui_ext));
- ui->ui_ext[kUINewgrid] = true;
+ ui->ui_ext[kUILinegrid] = true;
return ui_bridge_attach(ui, tui_main, tui_scheduler);
}
@@ -438,15 +439,16 @@ static void sigwinch_cb(SignalWatcher *watcher, int signum, void *data)
static bool attrs_differ(HlAttrs a1, HlAttrs a2, bool rgb)
{
if (rgb) {
- // TODO(bfredl): when we start to support special color,
- // rgb_sp_color must be added here
return a1.rgb_fg_color != a2.rgb_fg_color
|| a1.rgb_bg_color != a2.rgb_bg_color
- || a1.rgb_ae_attr != a2.rgb_ae_attr;
+ || a1.rgb_ae_attr != a2.rgb_ae_attr
+ || a1.rgb_sp_color != a2.rgb_sp_color;
} else {
return a1.cterm_fg_color != a2.cterm_fg_color
|| a1.cterm_bg_color != a2.cterm_bg_color
- || a1.cterm_ae_attr != a2.cterm_ae_attr;
+ || a1.cterm_ae_attr != a2.cterm_ae_attr
+ || (a1.cterm_ae_attr & (HL_UNDERLINE|HL_UNDERCURL)
+ && a1.rgb_sp_color != a2.rgb_sp_color);
}
}
@@ -483,12 +485,21 @@ static void update_attrs(UI *ui, HlAttrs attrs)
bool italic = attr & HL_ITALIC;
bool reverse = attr & HL_INVERSE;
bool standout = attr & HL_STANDOUT;
- bool underline = attr & (HL_UNDERLINE), undercurl = attr & (HL_UNDERCURL);
+
+ bool underline;
+ bool undercurl;
+ if (data->unibi_ext.enter_undercurl_mode) {
+ underline = attr & HL_UNDERLINE;
+ undercurl = attr & HL_UNDERCURL;
+ } else {
+ underline = (attr & HL_UNDERLINE) || (attr & HL_UNDERCURL);
+ undercurl = false;
+ }
if (unibi_get_str(data->ut, unibi_set_attributes)) {
- if (bold || reverse || underline || undercurl || standout) {
+ if (bold || reverse || underline || standout) {
UNIBI_SET_NUM_VAR(data->params[0], standout);
- UNIBI_SET_NUM_VAR(data->params[1], underline || undercurl);
+ UNIBI_SET_NUM_VAR(data->params[1], underline);
UNIBI_SET_NUM_VAR(data->params[2], reverse);
UNIBI_SET_NUM_VAR(data->params[3], 0); // blink
UNIBI_SET_NUM_VAR(data->params[4], 0); // dim
@@ -507,7 +518,7 @@ static void update_attrs(UI *ui, HlAttrs attrs)
if (bold) {
unibi_out(ui, unibi_enter_bold_mode);
}
- if (underline || undercurl) {
+ if (underline) {
unibi_out(ui, unibi_enter_underline_mode);
}
if (standout) {
@@ -520,6 +531,18 @@ static void update_attrs(UI *ui, HlAttrs attrs)
if (italic) {
unibi_out(ui, unibi_enter_italics_mode);
}
+ if (undercurl && data->unibi_ext.enter_undercurl_mode) {
+ unibi_out_ext(ui, data->unibi_ext.enter_undercurl_mode);
+ }
+ if ((undercurl || underline) && data->unibi_ext.set_underline_color) {
+ int color = attrs.rgb_sp_color;
+ if (color != -1) {
+ UNIBI_SET_NUM_VAR(data->params[0], (color >> 16) & 0xff); // red
+ UNIBI_SET_NUM_VAR(data->params[1], (color >> 8) & 0xff); // green
+ UNIBI_SET_NUM_VAR(data->params[2], color & 0xff); // blue
+ unibi_out_ext(ui, data->unibi_ext.set_underline_color);
+ }
+ }
if (ui->rgb) {
if (fg != -1) {
UNIBI_SET_NUM_VAR(data->params[0], (fg >> 16) & 0xff); // red
@@ -573,7 +596,7 @@ static void print_cell(UI *ui, UCell *ptr)
// Printing the next character finally advances the cursor.
final_column_wrap(ui);
}
- update_attrs(ui, ptr->attrs);
+ update_attrs(ui, kv_A(data->attrs, ptr->attr));
out(ui, ptr->data, strlen(ptr->data));
grid->col++;
if (data->immediate_wrap_after_last_column) {
@@ -589,7 +612,8 @@ static bool cheap_to_print(UI *ui, int row, int col, int next)
UCell *cell = grid->cells[row] + col;
while (next) {
next--;
- if (attrs_differ(cell->attrs, data->print_attrs, ui->rgb)) {
+ if (attrs_differ(kv_A(data->attrs, cell->attr),
+ data->print_attrs, ui->rgb)) {
if (data->default_attr) {
return false;
}
@@ -763,43 +787,31 @@ static void clear_region(UI *ui, int top, int bot, int left, int right,
cursor_goto(ui, data->row, data->col);
}
-static bool can_use_scroll(UI * ui)
+static void set_scroll_region(UI *ui, int top, int bot, int left, int right)
{
TUIData *data = ui->data;
UGrid *grid = &data->grid;
- return data->scroll_region_is_full_screen
- || (data->can_change_scroll_region
- && ((grid->left == 0 && grid->right == ui->width - 1)
- || data->can_set_lr_margin
- || data->can_set_left_right_margin));
-}
-
-static void set_scroll_region(UI *ui)
-{
- TUIData *data = ui->data;
- UGrid *grid = &data->grid;
-
- UNIBI_SET_NUM_VAR(data->params[0], grid->top);
- UNIBI_SET_NUM_VAR(data->params[1], grid->bot);
+ UNIBI_SET_NUM_VAR(data->params[0], top);
+ UNIBI_SET_NUM_VAR(data->params[1], bot);
unibi_out(ui, unibi_change_scroll_region);
- if (grid->left != 0 || grid->right != ui->width - 1) {
+ if (left != 0 || right != ui->width - 1) {
unibi_out_ext(ui, data->unibi_ext.enable_lr_margin);
if (data->can_set_lr_margin) {
- UNIBI_SET_NUM_VAR(data->params[0], grid->left);
- UNIBI_SET_NUM_VAR(data->params[1], grid->right);
+ UNIBI_SET_NUM_VAR(data->params[0], left);
+ UNIBI_SET_NUM_VAR(data->params[1], right);
unibi_out(ui, unibi_set_lr_margin);
} else {
- UNIBI_SET_NUM_VAR(data->params[0], grid->left);
+ UNIBI_SET_NUM_VAR(data->params[0], left);
unibi_out(ui, unibi_set_left_margin_parm);
- UNIBI_SET_NUM_VAR(data->params[0], grid->right);
+ UNIBI_SET_NUM_VAR(data->params[0], right);
unibi_out(ui, unibi_set_right_margin_parm);
}
}
unibi_goto(ui, grid->row, grid->col);
}
-static void reset_scroll_region(UI *ui)
+static void reset_scroll_region(UI *ui, bool fullwidth)
{
TUIData *data = ui->data;
UGrid *grid = &data->grid;
@@ -811,7 +823,7 @@ static void reset_scroll_region(UI *ui)
UNIBI_SET_NUM_VAR(data->params[1], ui->height - 1);
unibi_out(ui, unibi_change_scroll_region);
}
- if (grid->left != 0 || grid->right != ui->width - 1) {
+ if (!fullwidth) {
if (data->can_set_lr_margin) {
UNIBI_SET_NUM_VAR(data->params[0], 0);
UNIBI_SET_NUM_VAR(data->params[1], ui->width - 1);
@@ -848,7 +860,7 @@ static void tui_grid_resize(UI *ui, Integer g, Integer width, Integer height)
unibi_out_ext(ui, data->unibi_ext.resize_screen);
// DECSLPP does not reset the scroll region.
if (data->scroll_region_is_full_screen) {
- reset_scroll_region(ui);
+ reset_scroll_region(ui, ui->width == grid->width);
}
} else { // Already handled the SIGWINCH signal; avoid double-resize.
got_winch = false;
@@ -1006,28 +1018,35 @@ static void tui_mode_change(UI *ui, String mode, Integer mode_idx)
data->showing_mode = (ModeShape)mode_idx;
}
-static void tui_grid_scroll(UI *ui, Integer g, Integer top, Integer bot,
- Integer left, Integer right,
+static void tui_grid_scroll(UI *ui, Integer g, Integer startrow, Integer endrow,
+ Integer startcol, Integer endcol,
Integer rows, Integer cols)
{
TUIData *data = ui->data;
UGrid *grid = &data->grid;
- ugrid_set_scroll_region(&data->grid, (int)top, (int)bot-1,
- (int)left, (int)right-1);
+ int top = (int)startrow, bot = (int)endrow-1;
+ int left = (int)startcol, right = (int)endcol-1;
- data->scroll_region_is_full_screen =
- left == 0 && right == ui->width
- && top == 0 && bot == ui->height;
+ bool fullwidth = left == 0 && right == ui->width-1;
+ data->scroll_region_is_full_screen = fullwidth
+ && top == 0 && bot == ui->height-1;
int clear_top, clear_bot;
- ugrid_scroll(grid, (int)rows, &clear_top, &clear_bot);
+ ugrid_scroll(grid, top, bot, left, right, (int)rows,
+ &clear_top, &clear_bot);
+
+ bool can_scroll = data->scroll_region_is_full_screen
+ || (data->can_change_scroll_region
+ && ((left == 0 && right == ui->width - 1)
+ || data->can_set_lr_margin
+ || data->can_set_left_right_margin));
- if (can_use_scroll(ui)) {
+ if (can_scroll) {
// Change terminal scroll region and move cursor to the top
if (!data->scroll_region_is_full_screen) {
- set_scroll_region(ui);
+ set_scroll_region(ui, top, bot, left, right);
}
- cursor_goto(ui, grid->top, grid->left);
+ cursor_goto(ui, top, left);
// also set default color attributes or some terminals can become funny
update_attrs(ui, data->clear_attrs);
@@ -1049,19 +1068,19 @@ static void tui_grid_scroll(UI *ui, Integer g, Integer top, Integer bot,
// Restore terminal scroll region and cursor
if (!data->scroll_region_is_full_screen) {
- reset_scroll_region(ui);
+ reset_scroll_region(ui, fullwidth);
}
cursor_goto(ui, data->row, data->col);
if (!(data->bce || no_bg(ui, data->clear_attrs))) {
// Scrolling will leave wrong background in the cleared area on non-BCE
// terminals. Update the cleared area.
- clear_region(ui, clear_top, clear_bot, grid->left, grid->right,
+ clear_region(ui, clear_top, clear_bot, left, right,
data->clear_attrs);
}
} else {
// Mark the entire scroll region as invalid for redrawing later
- invalidate(ui, grid->top, grid->bot, grid->left, grid->right);
+ invalidate(ui, top, bot, left, right);
}
}
@@ -1220,7 +1239,8 @@ static void tui_raw_line(UI *ui, Integer g, Integer linerow, Integer startcol,
UGrid *grid = &data->grid;
for (Integer c = startcol; c < endcol; c++) {
memcpy(grid->cells[linerow][c].data, chunk[c-startcol], sizeof(schar_T));
- grid->cells[linerow][c].attrs = kv_A(data->attrs, attrs[c-startcol]);
+ assert((size_t)attrs[c-startcol] < kv_size(data->attrs));
+ grid->cells[linerow][c].attr = attrs[c-startcol];
}
UGRID_FOREACH_CELL(grid, (int)linerow, (int)linerow, (int)startcol,
(int)endcol-1, {
@@ -1231,7 +1251,7 @@ static void tui_raw_line(UI *ui, Integer g, Integer linerow, Integer startcol,
if (clearcol > endcol) {
HlAttrs cl_attrs = kv_A(data->attrs, (size_t)clearattr);
ugrid_clear_chunk(grid, (int)linerow, (int)endcol, (int)clearcol,
- cl_attrs);
+ (sattr_T)clearattr);
clear_region(ui, (int)linerow, (int)linerow, (int)endcol, (int)clearcol-1,
cl_attrs);
}
@@ -1419,6 +1439,18 @@ static int unibi_find_ext_str(unibi_term *ut, const char *name)
return -1;
}
+static int unibi_find_ext_bool(unibi_term *ut, const char *name)
+{
+ size_t max = unibi_count_ext_bool(ut);
+ for (size_t i = 0; i < max; i++) {
+ const char * n = unibi_get_ext_bool_name(ut, i);
+ if (n && 0 == strcmp(n, name)) {
+ return (int)i;
+ }
+ }
+ return -1;
+}
+
/// Patches the terminfo records after loading from system or built-in db.
/// Several entries in terminfo are known to be deficient or outright wrong;
/// and several terminal emulators falsely announce incorrect terminal types.
@@ -1446,6 +1478,7 @@ static void patch_terminfo_bugs(TUIData *data, const char *term,
|| terminfo_is_term_family(term, "iterm2")
|| terminfo_is_term_family(term, "iTerm.app")
|| terminfo_is_term_family(term, "iTerm2.app");
+ bool alacritty = terminfo_is_term_family(term, "alacritty");
// None of the following work over SSH; see :help TERM .
bool iterm_pretending_xterm = xterm && iterm_env;
bool konsole_pretending_xterm = xterm && konsole;
@@ -1640,6 +1673,7 @@ static void patch_terminfo_bugs(TUIData *data, const char *term,
// per analysis of VT100Terminal.m
|| iterm || iterm_pretending_xterm
|| teraterm // per TeraTerm "Supported Control Functions" doco
+ || alacritty // https://github.com/jwilm/alacritty/pull/608
// Some linux-type terminals implement the xterm extension.
// Example: console-terminal-emulator from the nosh toolset.
|| (linuxvt
@@ -1817,6 +1851,22 @@ static void augment_terminfo(TUIData *data, const char *term,
ut, "ext.enable_mouse", "\x1b[?1002h\x1b[?1006h");
data->unibi_ext.disable_mouse = (int)unibi_add_ext_str(
ut, "ext.disable_mouse", "\x1b[?1002l\x1b[?1006l");
+
+ int ext_bool_Su = unibi_find_ext_bool(ut, "Su"); // used by kitty
+ if (vte_version >= 5102
+ || (ext_bool_Su != -1 && unibi_get_ext_bool(ut, (size_t)ext_bool_Su))) {
+ data->unibi_ext.enter_undercurl_mode = (int)unibi_add_ext_str(
+ ut, "ext.enter_undercurl_mode", "\x1b[4:3m");
+ data->unibi_ext.exit_undercurl_mode = (int)unibi_add_ext_str(
+ ut, "ext.exit_undercurl_mode", "\x1b[4:0m");
+ if (has_colon_rgb) {
+ data->unibi_ext.set_underline_color = (int)unibi_add_ext_str(
+ ut, "ext.set_underline_color", "\x1b[58:2:%p1%d:%p2%d:%p3%dm");
+ } else {
+ data->unibi_ext.set_underline_color = (int)unibi_add_ext_str(
+ ut, "ext.set_underline_color", "\x1b[58:2:%p1%d:%p2%d:%p3%dm");
+ }
+ }
}
static void flush_buf(UI *ui)
diff --git a/src/nvim/ugrid.c b/src/nvim/ugrid.c
index 36936970f8..e2b92d7112 100644
--- a/src/nvim/ugrid.c
+++ b/src/nvim/ugrid.c
@@ -16,7 +16,6 @@
void ugrid_init(UGrid *grid)
{
- grid->attrs = HLATTRS_INIT;
grid->cells = NULL;
}
@@ -33,10 +32,6 @@ void ugrid_resize(UGrid *grid, int width, int height)
grid->cells[i] = xcalloc((size_t)width, sizeof(UCell));
}
- grid->top = 0;
- grid->bot = height - 1;
- grid->left = 0;
- grid->right = width - 1;
grid->row = grid->col = 0;
grid->width = width;
grid->height = height;
@@ -44,13 +39,12 @@ void ugrid_resize(UGrid *grid, int width, int height)
void ugrid_clear(UGrid *grid)
{
- clear_region(grid, 0, grid->height-1, 0, grid->width-1,
- HLATTRS_INIT);
+ clear_region(grid, 0, grid->height-1, 0, grid->width-1, 0);
}
-void ugrid_clear_chunk(UGrid *grid, int row, int col, int endcol, HlAttrs attrs)
+void ugrid_clear_chunk(UGrid *grid, int row, int col, int endcol, sattr_T attr)
{
- clear_region(grid, row, row, col, endcol-1, attrs);
+ clear_region(grid, row, row, col, endcol-1, attr);
}
void ugrid_goto(UGrid *grid, int row, int col)
@@ -59,25 +53,18 @@ void ugrid_goto(UGrid *grid, int row, int col)
grid->col = col;
}
-void ugrid_set_scroll_region(UGrid *grid, int top, int bot, int left, int right)
-{
- grid->top = top;
- grid->bot = bot;
- grid->left = left;
- grid->right = right;
-}
-
-void ugrid_scroll(UGrid *grid, int count, int *clear_top, int *clear_bot)
+void ugrid_scroll(UGrid *grid, int top, int bot, int left, int right,
+ int count, int *clear_top, int *clear_bot)
{
// Compute start/stop/step for the loop below
int start, stop, step;
if (count > 0) {
- start = grid->top;
- stop = grid->bot - count + 1;
+ start = top;
+ stop = bot - count + 1;
step = 1;
} else {
- start = grid->bot;
- stop = grid->top - count - 1;
+ start = bot;
+ stop = top - count - 1;
step = -1;
}
@@ -85,10 +72,10 @@ void ugrid_scroll(UGrid *grid, int count, int *clear_top, int *clear_bot)
// Copy cell data
for (i = start; i != stop; i += step) {
- UCell *target_row = grid->cells[i] + grid->left;
- UCell *source_row = grid->cells[i + count] + grid->left;
+ UCell *target_row = grid->cells[i] + left;
+ UCell *source_row = grid->cells[i + count] + left;
memcpy(target_row, source_row,
- sizeof(UCell) * (size_t)(grid->right - grid->left + 1));
+ sizeof(UCell) * (size_t)(right - left + 1));
}
// clear cells in the emptied region,
@@ -99,32 +86,16 @@ void ugrid_scroll(UGrid *grid, int count, int *clear_top, int *clear_bot)
*clear_bot = stop;
*clear_top = stop + count + 1;
}
- clear_region(grid, *clear_top, *clear_bot, grid->left, grid->right,
- HLATTRS_INIT);
-}
-
-UCell *ugrid_put(UGrid *grid, uint8_t *text, size_t size)
-{
- UCell *cell = grid->cells[grid->row] + grid->col;
- cell->data[size] = 0;
- cell->attrs = grid->attrs;
- assert(size <= CELLBYTES);
-
- if (text) {
- memcpy(cell->data, text, size);
- }
-
- grid->col += 1;
- return cell;
+ clear_region(grid, *clear_top, *clear_bot, left, right, 0);
}
static void clear_region(UGrid *grid, int top, int bot, int left, int right,
- HlAttrs attrs)
+ sattr_T attr)
{
UGRID_FOREACH_CELL(grid, top, bot, left, right, {
cell->data[0] = ' ';
cell->data[1] = 0;
- cell->attrs = attrs;
+ cell->attr = attr;
});
}
diff --git a/src/nvim/ugrid.h b/src/nvim/ugrid.h
index 04e027bd46..af78fe91c5 100644
--- a/src/nvim/ugrid.h
+++ b/src/nvim/ugrid.h
@@ -11,14 +11,12 @@ typedef struct ugrid UGrid;
struct ucell {
char data[CELLBYTES + 1];
- HlAttrs attrs;
+ sattr_T attr;
};
struct ugrid {
- int top, bot, left, right;
int row, col;
int width, height;
- HlAttrs attrs;
UCell **cells;
};
diff --git a/src/nvim/ui.h b/src/nvim/ui.h
index df489f569f..d89ad60ce7 100644
--- a/src/nvim/ui.h
+++ b/src/nvim/ui.h
@@ -15,7 +15,7 @@ typedef enum {
kUITabline,
kUIWildmenu,
#define kUIGlobalCount (kUIWildmenu+1)
- kUINewgrid,
+ kUILinegrid,
kUIHlState,
kUIExtCount,
} UIExtension;
@@ -25,7 +25,7 @@ EXTERN const char *ui_ext_names[] INIT(= {
"ext_popupmenu",
"ext_tabline",
"ext_wildmenu",
- "ext_newgrid",
+ "ext_linegrid",
"ext_hlstate",
});
diff --git a/src/nvim/undo.c b/src/nvim/undo.c
index 1e6be5d824..df0507ed41 100644
--- a/src/nvim/undo.c
+++ b/src/nvim/undo.c
@@ -891,7 +891,7 @@ static u_header_T *unserialize_uhp(bufinfo_T *bi,
for (;; ) {
int len = undo_read_byte(bi);
- if (len == 0) {
+ if (len == 0 || len == EOF) {
break;
}
int what = undo_read_byte(bi);