aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/README.md5
-rw-r--r--src/nvim/api/private/helpers.c69
-rw-r--r--src/nvim/api/private/helpers.h16
-rw-r--r--src/nvim/eval.c13
-rw-r--r--src/nvim/eval/typval.h2
-rw-r--r--src/nvim/ex_cmds2.c3
-rw-r--r--src/nvim/ex_docmd.c1
-rw-r--r--src/nvim/ex_eval.c4
-rw-r--r--src/nvim/ex_getln.c489
-rw-r--r--src/nvim/fileio.c11
-rw-r--r--src/nvim/log.c25
-rw-r--r--src/nvim/log.h1
-rw-r--r--src/nvim/lua/executor.c2
-rw-r--r--src/nvim/main.c1
-rw-r--r--src/nvim/mbyte.c2
-rw-r--r--src/nvim/mbyte.h6
-rw-r--r--src/nvim/memfile.c1
-rw-r--r--src/nvim/memline.c2
-rw-r--r--src/nvim/menu.c8
-rw-r--r--src/nvim/message.c21
-rw-r--r--src/nvim/os/fs.c14
-rw-r--r--src/nvim/regexp_nfa.c16
-rw-r--r--src/nvim/screen.c67
-rw-r--r--src/nvim/testdir/Makefile2
-rw-r--r--src/nvim/testdir/test_breakindent.vim298
-rw-r--r--src/nvim/testdir/test_display.vim71
-rw-r--r--src/nvim/testdir/test_listlbr.vim235
-rw-r--r--src/nvim/testdir/test_listlbr_utf8.vim229
-rw-r--r--src/nvim/testdir/test_window_cmd.vim44
-rw-r--r--src/nvim/testdir/view_util.vim30
-rw-r--r--src/nvim/tui/tui.c92
-rw-r--r--src/nvim/ui.c5
-rw-r--r--src/nvim/version.c24
-rw-r--r--src/nvim/window.c5
34 files changed, 1596 insertions, 218 deletions
diff --git a/src/nvim/README.md b/src/nvim/README.md
index 1c1c3c364e..0caf71e2c5 100644
--- a/src/nvim/README.md
+++ b/src/nvim/README.md
@@ -11,8 +11,9 @@ Logs
Low-level log messages sink to `$NVIM_LOG_FILE`.
-You can use `LOG_CALLSTACK()` anywhere in the source to log the current
-stacktrace. (Currently Linux-only.)
+You can use `LOG_CALLSTACK();` anywhere in the source to log the current
+stacktrace. To log in an alternate file, e.g. stderr, use
+`LOG_CALLSTACK_TO_FILE(FILE*)`. (Currently Linux-only.)
UI events are logged at level 0 (`DEBUG_LOG_LEVEL`).
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index 1ed2bc013e..e736e29e2d 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -37,7 +37,72 @@ typedef struct {
# include "api/private/ui_events_metadata.generated.h"
#endif
+/// Start block that may cause VimL exceptions while evaluating another code
+///
+/// Used when caller is supposed to be operating when other VimL code is being
+/// processed and that “other VimL code” must not be affected.
+///
+/// @param[out] tstate Location where try state should be saved.
+void try_enter(TryState *const tstate)
+{
+ *tstate = (TryState) {
+ .current_exception = current_exception,
+ .msg_list = (const struct msglist *const *)msg_list,
+ .private_msg_list = NULL,
+ .trylevel = trylevel,
+ .got_int = got_int,
+ .did_throw = did_throw,
+ .need_rethrow = need_rethrow,
+ .did_emsg = did_emsg,
+ };
+ msg_list = &tstate->private_msg_list;
+ current_exception = NULL;
+ trylevel = 1;
+ got_int = false;
+ did_throw = false;
+ need_rethrow = false;
+ did_emsg = false;
+}
+
+/// End try block, set the error message if any and restore previous state
+///
+/// @warning Return is consistent with most functions (false on error), not with
+/// try_end (true on error).
+///
+/// @param[in] tstate Previous state to restore.
+/// @param[out] err Location where error should be saved.
+///
+/// @return false if error occurred, true otherwise.
+bool try_leave(const TryState *const tstate, Error *const err)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ const bool ret = !try_end(err);
+ assert(trylevel == 0);
+ assert(!need_rethrow);
+ assert(!got_int);
+ assert(!did_throw);
+ assert(!did_emsg);
+ assert(msg_list == &tstate->private_msg_list);
+ assert(*msg_list == NULL);
+ assert(current_exception == NULL);
+ msg_list = (struct msglist **)tstate->msg_list;
+ current_exception = tstate->current_exception;
+ trylevel = tstate->trylevel;
+ got_int = tstate->got_int;
+ did_throw = tstate->did_throw;
+ need_rethrow = tstate->need_rethrow;
+ did_emsg = tstate->did_emsg;
+ return ret;
+}
+
/// Start block that may cause vimscript exceptions
+///
+/// Each try_start() call should be mirrored by try_end() call.
+///
+/// To be used as a replacement of `:try … catch … endtry` in C code, in cases
+/// when error flag could not already be set. If there may be pending error
+/// state at the time try_start() is executed which needs to be preserved,
+/// try_enter()/try_leave() pair should be used instead.
void try_start(void)
{
++trylevel;
@@ -50,7 +115,9 @@ void try_start(void)
/// @return true if an error occurred
bool try_end(Error *err)
{
- --trylevel;
+ // Note: all globals manipulated here should be saved/restored in
+ // try_enter/try_leave.
+ trylevel--;
// Without this it stops processing all subsequent VimL commands and
// generates strange error messages if I e.g. try calling Test() in a
diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h
index 159b9d5c2a..87f334ac30 100644
--- a/src/nvim/api/private/helpers.h
+++ b/src/nvim/api/private/helpers.h
@@ -6,6 +6,7 @@
#include "nvim/api/private/defs.h"
#include "nvim/vim.h"
#include "nvim/memory.h"
+#include "nvim/ex_eval.h"
#include "nvim/lib/kvec.h"
#define OBJECT_OBJ(o) o
@@ -82,6 +83,21 @@
#define api_free_window(value)
#define api_free_tabpage(value)
+/// Structure used for saving state for :try
+///
+/// Used when caller is supposed to be operating when other VimL code is being
+/// processed and that “other VimL code” must not be affected.
+typedef struct {
+ except_T *current_exception;
+ struct msglist *private_msg_list;
+ const struct msglist *const *msg_list;
+ int trylevel;
+ int got_int;
+ int did_throw;
+ int need_rethrow;
+ int did_emsg;
+} TryState;
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/private/helpers.h.generated.h"
#endif
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index c42929ef7c..ac22d75a83 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -11031,6 +11031,7 @@ void get_user_input(const typval_T *const argvars,
const char *defstr = "";
const char *cancelreturn = NULL;
const char *xp_name = NULL;
+ Callback input_callback = { .type = kCallbackNone };
char prompt_buf[NUMBUFLEN];
char defstr_buf[NUMBUFLEN];
char cancelreturn_buf[NUMBUFLEN];
@@ -11040,7 +11041,7 @@ void get_user_input(const typval_T *const argvars,
emsgf(_("E5050: {opts} must be the only argument"));
return;
}
- const dict_T *const dict = argvars[0].vval.v_dict;
+ dict_T *const dict = argvars[0].vval.v_dict;
prompt = tv_dict_get_string_buf_chk(dict, S_LEN("prompt"), prompt_buf, "");
if (prompt == NULL) {
return;
@@ -11066,6 +11067,9 @@ void get_user_input(const typval_T *const argvars,
if (xp_name == def) { // default to NULL
xp_name = NULL;
}
+ if (!tv_dict_get_callback(dict, S_LEN("highlight"), &input_callback)) {
+ return;
+ }
} else {
prompt = tv_get_string_buf_chk(&argvars[0], prompt_buf);
if (prompt == NULL) {
@@ -11124,12 +11128,13 @@ void get_user_input(const typval_T *const argvars,
stuffReadbuffSpec(defstr);
- int save_ex_normal_busy = ex_normal_busy;
+ const int save_ex_normal_busy = ex_normal_busy;
ex_normal_busy = 0;
rettv->vval.v_string =
- getcmdline_prompt(inputsecret_flag ? NUL : '@', (char_u *)p, echo_attr,
- xp_type, (char_u *)xp_arg);
+ (char_u *)getcmdline_prompt(inputsecret_flag ? NUL : '@', p, echo_attr,
+ xp_type, xp_arg, input_callback);
ex_normal_busy = save_ex_normal_busy;
+ callback_free(&input_callback);
if (rettv->vval.v_string == NULL && cancelreturn != NULL) {
rettv->vval.v_string = (char_u *)xstrdup(cancelreturn);
diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h
index 3f8ed3b3f9..c44b85644d 100644
--- a/src/nvim/eval/typval.h
+++ b/src/nvim/eval/typval.h
@@ -43,7 +43,7 @@ typedef struct partial_S partial_T;
typedef struct ufunc ufunc_T;
typedef enum {
- kCallbackNone,
+ kCallbackNone = 0,
kCallbackFuncref,
kCallbackPartial,
} CallbackType;
diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c
index 0fd74ccde7..33fe30cd5a 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -189,7 +189,8 @@ void do_debug(char_u *cmd)
}
xfree(cmdline);
- cmdline = getcmdline_prompt('>', NULL, 0, EXPAND_NOTHING, NULL);
+ cmdline = (char_u *)getcmdline_prompt('>', NULL, 0, EXPAND_NOTHING, NULL,
+ CALLBACK_NONE);
if (typeahead_saved) {
restore_typeahead(&typeaheadbuf);
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 29788a9865..6e7938046a 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -7690,6 +7690,7 @@ static void ex_redraw(exarg_T *eap)
RedrawingDisabled = 0;
p_lz = FALSE;
+ validate_cursor();
update_topline();
update_screen(eap->forceit ? CLEAR :
VIsual_active ? INVERTED :
diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c
index c029df2f13..9037b3c151 100644
--- a/src/nvim/ex_eval.c
+++ b/src/nvim/ex_eval.c
@@ -561,7 +561,9 @@ static void discard_exception(except_T *excp, int was_finished)
*/
void discard_current_exception(void)
{
- discard_exception(current_exception, FALSE);
+ discard_exception(current_exception, false);
+ // Note: all globals manipulated here should be saved/restored in
+ // try_enter/try_leave.
current_exception = NULL;
did_throw = FALSE;
need_rethrow = FALSE;
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index ecd5c81822..5e216925df 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -63,6 +63,9 @@
#include "nvim/os/os.h"
#include "nvim/event/loop.h"
#include "nvim/os/time.h"
+#include "nvim/lib/kvec.h"
+#include "nvim/api/private/helpers.h"
+#include "nvim/highlight_defs.h"
/*
* Variables shared between getcmdline(), redrawcmdline() and others.
@@ -70,23 +73,27 @@
* structure.
*/
struct cmdline_info {
- char_u *cmdbuff; /* pointer to command line buffer */
- int cmdbufflen; /* length of cmdbuff */
- int cmdlen; /* number of chars in command line */
- int cmdpos; /* current cursor position */
- int cmdspos; /* cursor column on screen */
- int cmdfirstc; /* ':', '/', '?', '=', '>' or NUL */
- int cmdindent; /* number of spaces before cmdline */
- char_u *cmdprompt; /* message in front of cmdline */
- int cmdattr; /* attributes for prompt */
- int overstrike; /* Typing mode on the command line. Shared by
- getcmdline() and put_on_cmdline(). */
- expand_T *xpc; /* struct being used for expansion, xp_pattern
- may point into cmdbuff */
- int xp_context; /* type of expansion */
- char_u *xp_arg; /* user-defined expansion arg */
- int input_fn; /* when TRUE Invoked for input() function */
+ char_u *cmdbuff; // pointer to command line buffer
+ int cmdbufflen; // length of cmdbuff
+ int cmdlen; // number of chars in command line
+ int cmdpos; // current cursor position
+ int cmdspos; // cursor column on screen
+ int cmdfirstc; // ':', '/', '?', '=', '>' or NUL
+ int cmdindent; // number of spaces before cmdline
+ char_u *cmdprompt; // message in front of cmdline
+ int cmdattr; // attributes for prompt
+ int overstrike; // Typing mode on the command line. Shared by
+ // getcmdline() and put_on_cmdline().
+ expand_T *xpc; // struct being used for expansion, xp_pattern
+ // may point into cmdbuff
+ int xp_context; // type of expansion
+ char_u *xp_arg; // user-defined expansion arg
+ int input_fn; // when TRUE Invoked for input() function
+ unsigned prompt_id; ///< Prompt number, used to disable coloring on errors.
+ Callback highlight_callback; ///< Callback used for coloring user input.
};
+/// Last value of prompt_id, incremented when doing new prompt
+static unsigned last_prompt_id = 0;
typedef struct command_line_state {
VimState state;
@@ -136,6 +143,38 @@ typedef struct command_line_state {
struct cmdline_info save_ccline;
} CommandLineState;
+/// Command-line colors: one chunk
+///
+/// Defines a region which has the same highlighting.
+typedef struct {
+ int start; ///< Colored chunk start.
+ int end; ///< Colored chunk end (exclusive, > start).
+ int attr; ///< Highlight attr.
+} CmdlineColorChunk;
+
+/// Command-line colors
+///
+/// Holds data about all colors.
+typedef kvec_t(CmdlineColorChunk) CmdlineColors;
+
+/// Command-line coloring
+///
+/// Holds both what are the colors and what have been colored. Latter is used to
+/// suppress unnecessary calls to coloring callbacks.
+typedef struct {
+ unsigned prompt_id; ///< ID of the prompt which was colored last.
+ char *cmdbuff; ///< What exactly was colored last time or NULL.
+ CmdlineColors colors; ///< Last colors.
+} ColoredCmdline;
+
+/// Last command-line colors.
+ColoredCmdline last_ccline_colors = {
+ .cmdbuff = NULL,
+ .colors = KV_INITIAL_VALUE
+};
+
+typedef struct cmdline_info CmdlineInfo;
+
/* The current cmdline_info. It is initialized in getcmdline() and after that
* used by other functions. When invoking getcmdline() recursively it needs
* to be saved with save_cmdline() and restored with restore_cmdline().
@@ -157,6 +196,12 @@ static int hisnum[HIST_COUNT] = {0, 0, 0, 0, 0};
/* identifying (unique) number of newest history entry */
static int hislen = 0; /* actual length of history tables */
+/// Flag for command_line_handle_key to ignore <C-c>
+///
+/// Used if it was received while processing highlight function in order for
+/// user interrupting highlight function to not interrupt command-line.
+static bool getln_interrupted_highlight = false;
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ex_getln.c.generated.h"
@@ -193,6 +238,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
cmd_hkmap = 0;
}
+ ccline.prompt_id = last_prompt_id++;
ccline.overstrike = false; // always start in insert mode
clearpos(&s->match_end);
s->save_cursor = curwin->w_cursor; // may be restored later
@@ -1160,8 +1206,11 @@ static int command_line_handle_key(CommandLineState *s)
case ESC: // get here if p_wc != ESC or when ESC typed twice
case Ctrl_C:
// In exmode it doesn't make sense to return. Except when
- // ":normal" runs out of characters.
- if (exmode_active && (ex_normal_busy == 0 || typebuf.tb_len > 0)) {
+ // ":normal" runs out of characters. Also when highlight callback is active
+ // <C-c> should interrupt only it.
+ if ((exmode_active && (ex_normal_busy == 0 || typebuf.tb_len > 0))
+ || (getln_interrupted_highlight && s->c == Ctrl_C)) {
+ getln_interrupted_highlight = false;
return command_line_not_changed(s);
}
@@ -1790,41 +1839,50 @@ getcmdline (
return command_line_enter(firstc, count, indent);
}
-/*
- * Get a command line with a prompt.
- * This is prepared to be called recursively from getcmdline() (e.g. by
- * f_input() when evaluating an expression from CTRL-R =).
- * Returns the command line in allocated memory, or NULL.
- */
-char_u *
-getcmdline_prompt (
- int firstc,
- char_u *prompt, /* command line prompt */
- int attr, /* attributes for prompt */
- int xp_context, /* type of expansion */
- char_u *xp_arg /* user-defined expansion argument */
-)
+/// Get a command line with a prompt
+///
+/// This is prepared to be called recursively from getcmdline() (e.g. by
+/// f_input() when evaluating an expression from `<C-r>=`).
+///
+/// @param[in] firstc Prompt type: e.g. '@' for input(), '>' for debug.
+/// @param[in] prompt Prompt string: what is displayed before the user text.
+/// @param[in] attr Prompt highlighting.
+/// @param[in] xp_context Type of expansion.
+/// @param[in] xp_arg User-defined expansion argument.
+/// @param[in] highlight_callback Callback used for highlighting user input.
+///
+/// @return [allocated] Command line or NULL.
+char *getcmdline_prompt(const char firstc, const char *const prompt,
+ const int attr, const int xp_context,
+ const char *const xp_arg,
+ const Callback highlight_callback)
+ FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC
{
- char_u *s;
- struct cmdline_info save_ccline;
- int msg_col_save = msg_col;
+ const int msg_col_save = msg_col;
+ struct cmdline_info save_ccline;
save_cmdline(&save_ccline);
- ccline.cmdprompt = prompt;
+
+ ccline.prompt_id = last_prompt_id++;
+ ccline.cmdprompt = (char_u *)prompt;
ccline.cmdattr = attr;
ccline.xp_context = xp_context;
- ccline.xp_arg = xp_arg;
+ ccline.xp_arg = (char_u *)xp_arg;
ccline.input_fn = (firstc == '@');
- s = getcmdline(firstc, 1L, 0);
+ ccline.highlight_callback = highlight_callback;
+
+ char *const ret = (char *)getcmdline(firstc, 1L, 0);
+
restore_cmdline(&save_ccline);
- /* Restore msg_col, the prompt from input() may have changed it.
- * But only if called recursively and the commandline is therefore being
- * restored to an old one; if not, the input() prompt stays on the screen,
- * so we need its modified msg_col left intact. */
- if (ccline.cmdbuff != NULL)
+ // Restore msg_col, the prompt from input() may have changed it.
+ // But only if called recursively and the commandline is therefore being
+ // restored to an old one; if not, the input() prompt stays on the screen,
+ // so we need its modified msg_col left intact.
+ if (ccline.cmdbuff != NULL) {
msg_col = msg_col_save;
+ }
- return s;
+ return ret;
}
/*
@@ -2285,75 +2343,329 @@ void free_cmdline_buf(void)
# endif
+enum { MAX_CB_ERRORS = 1 };
+
+/// Color command-line
+///
+/// Should use built-in command parser or user-specified one. Currently only the
+/// latter is supported.
+///
+/// @param[in] colored_ccline Command-line to color.
+/// @param[out] ret_ccline_colors What should be colored. Also holds a cache:
+/// if ->prompt_id and ->cmdbuff values happen
+/// to be equal to those from colored_cmdline it
+/// will just do nothing, assuming that ->colors
+/// already contains needed data.
+///
+/// Always colors the whole cmdline.
+///
+/// @return true if draw_cmdline may proceed, false if it does not need anything
+/// to do.
+static bool color_cmdline(const CmdlineInfo *const colored_ccline,
+ ColoredCmdline *const ret_ccline_colors)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ bool printed_errmsg = false;
+#define PRINT_ERRMSG(...) \
+ do { \
+ msg_putchar('\n'); \
+ msg_printf_attr(hl_attr(HLF_E)|MSG_HIST, __VA_ARGS__); \
+ printed_errmsg = true; \
+ } while (0)
+ bool ret = true;
+
+ // Check whether result of the previous call is still valid.
+ if (ret_ccline_colors->prompt_id == colored_ccline->prompt_id
+ && ret_ccline_colors->cmdbuff != NULL
+ && STRCMP(ret_ccline_colors->cmdbuff, colored_ccline->cmdbuff) == 0) {
+ return ret;
+ }
+
+ kv_size(ret_ccline_colors->colors) = 0;
+
+ if (colored_ccline->cmdbuff == NULL || *colored_ccline->cmdbuff == NUL) {
+ // Nothing to do, exiting.
+ xfree(ret_ccline_colors->cmdbuff);
+ ret_ccline_colors->cmdbuff = NULL;
+ return ret;
+ }
+
+ bool arg_allocated = false;
+ typval_T arg = {
+ .v_type = VAR_STRING,
+ .vval.v_string = colored_ccline->cmdbuff,
+ };
+ typval_T tv = { .v_type = VAR_UNKNOWN };
+
+ static unsigned prev_prompt_id = UINT_MAX;
+ static int prev_prompt_errors = 0;
+ Callback color_cb = { .type = kCallbackNone };
+ bool can_free_cb = false;
+ TryState tstate;
+ Error err = ERROR_INIT;
+ const char *err_errmsg = (const char *)e_intern2;
+ bool dgc_ret = true;
+ bool tl_ret = true;
+
+ if (colored_ccline->prompt_id != prev_prompt_id) {
+ prev_prompt_errors = 0;
+ prev_prompt_id = colored_ccline->prompt_id;
+ } else if (prev_prompt_errors >= MAX_CB_ERRORS) {
+ goto color_cmdline_end;
+ }
+ if (colored_ccline->highlight_callback.type != kCallbackNone) {
+ // Currently this should only happen while processing input() prompts.
+ assert(colored_ccline->input_fn);
+ color_cb = colored_ccline->highlight_callback;
+ } else if (colored_ccline->cmdfirstc == ':') {
+ try_enter(&tstate);
+ err_errmsg = N_(
+ "E5408: Unable to get g:Nvim_color_cmdline callback: %s");
+ dgc_ret = tv_dict_get_callback(&globvardict, S_LEN("Nvim_color_cmdline"),
+ &color_cb);
+ tl_ret = try_leave(&tstate, &err);
+ can_free_cb = true;
+ } else if (colored_ccline->cmdfirstc == '=') {
+ try_enter(&tstate);
+ err_errmsg = N_(
+ "E5409: Unable to get g:Nvim_color_expr callback: %s");
+ dgc_ret = tv_dict_get_callback(&globvardict, S_LEN("Nvim_color_expr"),
+ &color_cb);
+ tl_ret = try_leave(&tstate, &err);
+ can_free_cb = true;
+ }
+ if (!tl_ret || !dgc_ret) {
+ goto color_cmdline_error;
+ }
+
+ if (color_cb.type == kCallbackNone) {
+ goto color_cmdline_end;
+ }
+ if (colored_ccline->cmdbuff[colored_ccline->cmdlen] != NUL) {
+ arg_allocated = true;
+ arg.vval.v_string = xmemdupz((const char *)colored_ccline->cmdbuff,
+ (size_t)colored_ccline->cmdlen);
+ }
+ // msg_start() called by e.g. :echo may shift command-line to the first column
+ // even though msg_silent is here. Two ways to workaround this problem without
+ // altering message.c: use full_screen or save and restore msg_col.
+ //
+ // Saving and restoring full_screen does not work well with :redraw!. Saving
+ // and restoring msg_col is neither ideal, but while with full_screen it
+ // appears shifted one character to the right and cursor position is no longer
+ // correct, with msg_col it just misses leading `:`. Since `redraw!` in
+ // callback lags this is least of the user problems.
+ //
+ // Also using try_enter() because error messages may overwrite typed
+ // command-line which is not expected.
+ getln_interrupted_highlight = false;
+ try_enter(&tstate);
+ err_errmsg = N_("E5407: Callback has thrown an exception: %s");
+ const int saved_msg_col = msg_col;
+ msg_silent++;
+ const bool cbcall_ret = callback_call(&color_cb, 1, &arg, &tv);
+ msg_silent--;
+ msg_col = saved_msg_col;
+ if (got_int) {
+ getln_interrupted_highlight = true;
+ }
+ if (!try_leave(&tstate, &err) || !cbcall_ret) {
+ goto color_cmdline_error;
+ }
+ if (tv.v_type != VAR_LIST) {
+ PRINT_ERRMSG(_("E5400: Callback should return list"));
+ goto color_cmdline_error;
+ }
+ if (tv.vval.v_list == NULL) {
+ goto color_cmdline_end;
+ }
+ varnumber_T prev_end = 0;
+ int i = 0;
+ for (const listitem_T *li = tv.vval.v_list->lv_first;
+ li != NULL; li = li->li_next, i++) {
+ if (li->li_tv.v_type != VAR_LIST) {
+ PRINT_ERRMSG(_("E5401: List item %i is not a List"), i);
+ goto color_cmdline_error;
+ }
+ const list_T *const l = li->li_tv.vval.v_list;
+ if (tv_list_len(l) != 3) {
+ PRINT_ERRMSG(_("E5402: List item %i has incorrect length: %li /= 3"),
+ i, tv_list_len(l));
+ goto color_cmdline_error;
+ }
+ bool error = false;
+ const varnumber_T start = tv_get_number_chk(&l->lv_first->li_tv, &error);
+ if (error) {
+ goto color_cmdline_error;
+ } else if (!(prev_end <= start && start < colored_ccline->cmdlen)) {
+ PRINT_ERRMSG(_("E5403: Chunk %i start %" PRIdVARNUMBER " not in range "
+ "[%" PRIdVARNUMBER ", %i)"),
+ i, start, prev_end, colored_ccline->cmdlen);
+ goto color_cmdline_error;
+ } else if (utf8len_tab_zero[(uint8_t)colored_ccline->cmdbuff[start]] == 0) {
+ PRINT_ERRMSG(_("E5405: Chunk %i start %" PRIdVARNUMBER " splits "
+ "multibyte character"), i, start);
+ goto color_cmdline_error;
+ }
+ if (start != prev_end) {
+ kv_push(ret_ccline_colors->colors, ((CmdlineColorChunk) {
+ .start = prev_end,
+ .end = start,
+ .attr = 0,
+ }));
+ }
+ const varnumber_T end = tv_get_number_chk(&l->lv_first->li_next->li_tv,
+ &error);
+ if (error) {
+ goto color_cmdline_error;
+ } else if (!(start < end && end <= colored_ccline->cmdlen)) {
+ PRINT_ERRMSG(_("E5404: Chunk %i end %" PRIdVARNUMBER " not in range "
+ "(%" PRIdVARNUMBER ", %i]"),
+ i, end, start, colored_ccline->cmdlen);
+ goto color_cmdline_error;
+ } else if (end < colored_ccline->cmdlen
+ && (utf8len_tab_zero[(uint8_t)colored_ccline->cmdbuff[end]]
+ == 0)) {
+ PRINT_ERRMSG(_("E5406: Chunk %i end %" PRIdVARNUMBER " splits multibyte "
+ "character"), i, end);
+ goto color_cmdline_error;
+ }
+ prev_end = end;
+ const char *const group = tv_get_string_chk(&l->lv_last->li_tv);
+ if (group == NULL) {
+ goto color_cmdline_error;
+ }
+ const int id = syn_name2id((char_u *)group);
+ const int attr = (id == 0 ? 0 : syn_id2attr(id));
+ kv_push(ret_ccline_colors->colors, ((CmdlineColorChunk) {
+ .start = start,
+ .end = end,
+ .attr = attr,
+ }));
+ }
+ if (prev_end < colored_ccline->cmdlen) {
+ kv_push(ret_ccline_colors->colors, ((CmdlineColorChunk) {
+ .start = prev_end,
+ .end = colored_ccline->cmdlen,
+ .attr = 0,
+ }));
+ }
+ prev_prompt_errors = 0;
+color_cmdline_end:
+ assert(!ERROR_SET(&err));
+ if (can_free_cb) {
+ callback_free(&color_cb);
+ }
+ xfree(ret_ccline_colors->cmdbuff);
+ // Note: errors “output” is cached just as well as regular results.
+ ret_ccline_colors->prompt_id = colored_ccline->prompt_id;
+ if (arg_allocated) {
+ ret_ccline_colors->cmdbuff = (char *)arg.vval.v_string;
+ } else {
+ ret_ccline_colors->cmdbuff = xmemdupz((const char *)colored_ccline->cmdbuff,
+ (size_t)colored_ccline->cmdlen);
+ }
+ tv_clear(&tv);
+ return ret;
+color_cmdline_error:
+ if (ERROR_SET(&err)) {
+ PRINT_ERRMSG(_(err_errmsg), err.msg);
+ api_clear_error(&err);
+ }
+ assert(printed_errmsg);
+ (void)printed_errmsg;
+
+ prev_prompt_errors++;
+ kv_size(ret_ccline_colors->colors) = 0;
+ redrawcmdline();
+ ret = false;
+ goto color_cmdline_end;
+#undef PRINT_ERRMSG
+}
+
/*
* Draw part of the cmdline at the current cursor position. But draw stars
* when cmdline_star is TRUE.
*/
static void draw_cmdline(int start, int len)
{
- int i;
+ if (!color_cmdline(&ccline, &last_ccline_colors)) {
+ return;
+ }
- if (cmdline_star > 0)
- for (i = 0; i < len; ++i) {
+ if (cmdline_star > 0) {
+ for (int i = 0; i < len; i++) {
msg_putchar('*');
- if (has_mbyte)
+ if (has_mbyte) {
i += (*mb_ptr2len)(ccline.cmdbuff + start + i) - 1;
+ }
}
- else if (p_arshape && !p_tbidi && enc_utf8 && len > 0) {
- static int buflen = 0;
- char_u *p;
- int j;
- int newlen = 0;
+ } else if (p_arshape && !p_tbidi && enc_utf8 && len > 0) {
+ bool do_arabicshape = false;
int mb_l;
- int pc, pc1 = 0;
- int prev_c = 0;
- int prev_c1 = 0;
- int u8c;
- int u8cc[MAX_MCO];
- int nc = 0;
+ for (int i = start; i < start + len; i += mb_l) {
+ char_u *p = ccline.cmdbuff + i;
+ int u8cc[MAX_MCO];
+ int u8c = utfc_ptr2char_len(p, u8cc, start + len - i);
+ mb_l = utfc_ptr2len_len(p, start + len - i);
+ if (arabic_char(u8c)) {
+ do_arabicshape = true;
+ break;
+ }
+ }
+ if (!do_arabicshape) {
+ goto draw_cmdline_no_arabicshape;
+ }
- /*
- * Do arabic shaping into a temporary buffer. This is very
- * inefficient!
- */
+ static int buflen = 0;
+
+ // Do arabic shaping into a temporary buffer. This is very
+ // inefficient!
if (len * 2 + 2 > buflen) {
- /* Re-allocate the buffer. We keep it around to avoid a lot of
- * alloc()/free() calls. */
+ // Re-allocate the buffer. We keep it around to avoid a lot of
+ // alloc()/free() calls.
xfree(arshape_buf);
buflen = len * 2 + 2;
arshape_buf = xmalloc(buflen);
}
+ int newlen = 0;
if (utf_iscomposing(utf_ptr2char(ccline.cmdbuff + start))) {
- /* Prepend a space to draw the leading composing char on. */
+ // Prepend a space to draw the leading composing char on.
arshape_buf[0] = ' ';
newlen = 1;
}
- for (j = start; j < start + len; j += mb_l) {
- p = ccline.cmdbuff + j;
- u8c = utfc_ptr2char_len(p, u8cc, start + len - j);
- mb_l = utfc_ptr2len_len(p, start + len - j);
+ int prev_c = 0;
+ int prev_c1 = 0;
+ for (int i = start; i < start + len; i += mb_l) {
+ char_u *p = ccline.cmdbuff + i;
+ int u8cc[MAX_MCO];
+ int u8c = utfc_ptr2char_len(p, u8cc, start + len - i);
+ mb_l = utfc_ptr2len_len(p, start + len - i);
if (arabic_char(u8c)) {
- /* Do Arabic shaping. */
+ int pc;
+ int pc1 = 0;
+ int nc = 0;
+ // Do Arabic shaping.
if (cmdmsg_rl) {
- /* displaying from right to left */
+ // Displaying from right to left.
pc = prev_c;
pc1 = prev_c1;
prev_c1 = u8cc[0];
- if (j + mb_l >= start + len)
+ if (i + mb_l >= start + len) {
nc = NUL;
- else
+ } else {
nc = utf_ptr2char(p + mb_l);
+ }
} else {
- /* displaying from left to right */
- if (j + mb_l >= start + len)
+ // Displaying from left to right.
+ if (i + mb_l >= start + len) {
pc = NUL;
- else {
+ } else {
int pcc[MAX_MCO];
- pc = utfc_ptr2char_len(p + mb_l, pcc,
- start + len - j - mb_l);
+ pc = utfc_ptr2char_len(p + mb_l, pcc, start + len - i - mb_l);
pc1 = pcc[0];
}
nc = prev_c;
@@ -2377,8 +2689,23 @@ static void draw_cmdline(int start, int len)
}
msg_outtrans_len(arshape_buf, newlen);
- } else
- msg_outtrans_len(ccline.cmdbuff + start, len);
+ } else {
+draw_cmdline_no_arabicshape:
+ if (kv_size(last_ccline_colors.colors)) {
+ for (size_t i = 0; i < kv_size(last_ccline_colors.colors); i++) {
+ CmdlineColorChunk chunk = kv_A(last_ccline_colors.colors, i);
+ if (chunk.end <= start) {
+ continue;
+ }
+ const int chunk_start = MAX(chunk.start, start);
+ msg_outtrans_len_attr(ccline.cmdbuff + chunk_start,
+ chunk.end - chunk_start,
+ chunk.attr);
+ }
+ } else {
+ msg_outtrans_len(ccline.cmdbuff + start, len);
+ }
+ }
}
/*
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index 643020df5e..feb16f44d4 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -2570,11 +2570,9 @@ buf_write (
perm = -1;
}
}
-#else /* win32 */
- /*
- * Check for a writable device name.
- */
- c = os_nodetype((char *)fname);
+#else // win32
+ // Check for a writable device name.
+ c = fname == NULL ? NODE_OTHER : os_nodetype((char *)fname);
if (c == NODE_OTHER) {
SET_ERRMSG_NUM("E503", _("is not a file or writable device"));
goto fail;
@@ -2594,9 +2592,8 @@ buf_write (
if (overwriting) {
os_fileinfo((char *)fname, &file_info_old);
}
-
}
-#endif /* !UNIX */
+#endif // !UNIX
if (!device && !newfile) {
/*
diff --git a/src/nvim/log.c b/src/nvim/log.c
index b64aef3cac..3baf0b2ebd 100644
--- a/src/nvim/log.c
+++ b/src/nvim/log.c
@@ -178,7 +178,8 @@ FILE *open_log_file(void)
}
#ifdef HAVE_EXECINFO_BACKTRACE
-void log_callstack(const char *const func_name, const int line_num)
+void log_callstack_to_file(FILE *log_file, const char *const func_name,
+ const int line_num)
{
void *trace[100];
int trace_size = backtrace(trace, ARRAY_SIZE(trace));
@@ -190,8 +191,6 @@ void log_callstack(const char *const func_name, const int line_num)
}
assert(24 + exepathlen < IOSIZE); // Must fit in `cmdbuf` below.
- do_log(DEBUG_LOG_LEVEL, func_name, line_num, true, "trace:");
-
char cmdbuf[IOSIZE + (20 * ARRAY_SIZE(trace))];
snprintf(cmdbuf, sizeof(cmdbuf), "addr2line -e %s -f -p", exepath);
for (int i = 1; i < trace_size; i++) {
@@ -202,12 +201,8 @@ void log_callstack(const char *const func_name, const int line_num)
// Now we have a command string like:
// addr2line -e /path/to/exe -f -p 0x123 0x456 ...
- log_lock();
- FILE *log_file = open_log_file();
- if (log_file == NULL) {
- goto end;
- }
-
+ do_log_to_file(log_file, DEBUG_LOG_LEVEL, func_name, line_num, true,
+ "trace:");
FILE *fp = popen(cmdbuf, "r");
char linebuf[IOSIZE];
while (fgets(linebuf, sizeof(linebuf) - 1, fp) != NULL) {
@@ -218,6 +213,18 @@ void log_callstack(const char *const func_name, const int line_num)
if (log_file != stderr && log_file != stdout) {
fclose(log_file);
}
+}
+
+void log_callstack(const char *const func_name, const int line_num)
+{
+ log_lock();
+ FILE *log_file = open_log_file();
+ if (log_file == NULL) {
+ goto end;
+ }
+
+ log_callstack_to_file(log_file, func_name, line_num);
+
end:
log_unlock();
}
diff --git a/src/nvim/log.h b/src/nvim/log.h
index 743a8d17aa..5064d9333b 100644
--- a/src/nvim/log.h
+++ b/src/nvim/log.h
@@ -63,6 +63,7 @@
#ifdef HAVE_EXECINFO_BACKTRACE
# define LOG_CALLSTACK() log_callstack(__func__, __LINE__)
+# define LOG_CALLSTACK_TO_FILE(fp) log_callstack_to_file(fp, __func__, __LINE__)
#endif
#ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index 9ec5bfb8ad..eb821f7831 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -519,7 +519,7 @@ static int nlua_print(lua_State *const lstate)
}
msg((char_u *)str + start);
}
- if (str[len - 1] == NUL) { // Last was newline
+ if (len && str[len - 1] == NUL) { // Last was newline
msg((char_u *)"");
}
}
diff --git a/src/nvim/main.c b/src/nvim/main.c
index a46c1a58f8..3f828d7be9 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -1825,6 +1825,7 @@ static int process_env(char *env, bool is_viminit)
/// os_fileinfo_link() respectively for extra security.
static bool file_owned(const char *fname)
{
+ assert(fname != NULL);
uid_t uid = getuid();
FileInfo file_info;
bool file_owned = os_fileinfo(fname, &file_info)
diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c
index 3fad6c789d..b24770a409 100644
--- a/src/nvim/mbyte.c
+++ b/src/nvim/mbyte.c
@@ -75,7 +75,7 @@ struct interval {
/*
* Like utf8len_tab above, but using a zero for illegal lead bytes.
*/
-static uint8_t utf8len_tab_zero[256] =
+const uint8_t utf8len_tab_zero[256] =
{
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
diff --git a/src/nvim/mbyte.h b/src/nvim/mbyte.h
index ad9e38004c..bf6ccb13a5 100644
--- a/src/nvim/mbyte.h
+++ b/src/nvim/mbyte.h
@@ -1,6 +1,7 @@
#ifndef NVIM_MBYTE_H
#define NVIM_MBYTE_H
+#include <stdint.h>
#include <stdbool.h>
#include <string.h>
@@ -18,6 +19,9 @@
#define MB_BYTE2LEN(b) utf8len_tab[b]
#define MB_BYTE2LEN_CHECK(b) (((b) < 0 || (b) > 255) ? 1 : utf8len_tab[b])
+// max length of an unicode char
+#define MB_MAXCHAR 6
+
/* properties used in enc_canon_table[] (first three mutually exclusive) */
#define ENC_8BIT 0x01
#define ENC_DBCS 0x02
@@ -67,6 +71,8 @@ typedef struct {
///< otherwise use '?'.
} vimconv_T;
+extern const uint8_t utf8len_tab_zero[256];
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "mbyte.h.generated.h"
#endif
diff --git a/src/nvim/memfile.c b/src/nvim/memfile.c
index 9429703620..4428dd42aa 100644
--- a/src/nvim/memfile.c
+++ b/src/nvim/memfile.c
@@ -895,6 +895,7 @@ static bool mf_do_open(memfile_T *mfp, char_u *fname, int flags)
{
// fname cannot be NameBuff, because it must have been allocated.
mf_set_fnames(mfp, fname);
+ assert(mfp->mf_fname != NULL);
/// Extra security check: When creating a swap file it really shouldn't
/// exist yet. If there is a symbolic link, this is most likely an attack.
diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index 55e7e01825..f28a9e60f4 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -1460,6 +1460,7 @@ static int process_still_running;
*/
static time_t swapfile_info(char_u *fname)
{
+ assert(fname != NULL);
int fd;
struct block0 b0;
time_t x = (time_t)0;
@@ -3135,6 +3136,7 @@ attention_message (
char_u *fname /* swap file name */
)
{
+ assert(buf->b_fname != NULL);
time_t x, sx;
char *p;
diff --git a/src/nvim/menu.c b/src/nvim/menu.c
index a498916e5e..0db250d111 100644
--- a/src/nvim/menu.c
+++ b/src/nvim/menu.c
@@ -666,8 +666,6 @@ static void free_menu_string(vimmenu_T *menu, int idx)
static dict_T *menu_get_recursive(const vimmenu_T *menu, int modes)
{
dict_T *dict;
- char buf[sizeof(menu->mnemonic)];
- int mnemonic_len;
if (!menu || (menu->modes & modes) == 0x0) {
return NULL;
@@ -679,8 +677,8 @@ static dict_T *menu_get_recursive(const vimmenu_T *menu, int modes)
tv_dict_add_nr(dict, S_LEN("hidden"), menu_is_hidden(menu->dname));
if (menu->mnemonic) {
- mnemonic_len = utf_char2bytes(menu->mnemonic, (u_char *)buf);
- buf[mnemonic_len] = '\0';
+ char buf[MB_MAXCHAR + 1] = { 0 }; // > max value of utf8_char2bytes
+ utf_char2bytes(menu->mnemonic, (char_u *)buf);
tv_dict_add_str(dict, S_LEN("shortcut"), buf);
}
@@ -717,7 +715,7 @@ static dict_T *menu_get_recursive(const vimmenu_T *menu, int modes)
list_T *children_list = tv_list_alloc();
for (menu = menu->children; menu != NULL; menu = menu->next) {
dict_T *dic = menu_get_recursive(menu, modes);
- if (dict && tv_dict_len(dict) > 0) {
+ if (tv_dict_len(dict) > 0) {
tv_list_append_dict(children_list, dic);
}
}
diff --git a/src/nvim/message.c b/src/nvim/message.c
index 28c88f5a14..b90c475ede 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -1628,6 +1628,27 @@ void msg_puts_attr_len(const char *const str, const ptrdiff_t len, int attr)
}
}
+/// Print a formatted message
+///
+/// Message printed is limited by #IOSIZE. Must not be used from inside
+/// msg_puts_attr().
+///
+/// @param[in] attr Highlight attributes.
+/// @param[in] fmt Format string.
+void msg_printf_attr(const int attr, const char *const fmt, ...)
+ FUNC_ATTR_NONNULL_ARG(2)
+{
+ static char msgbuf[IOSIZE];
+
+ va_list ap;
+ va_start(ap, fmt);
+ const size_t len = vim_vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap, NULL);
+ va_end(ap);
+
+ msg_scroll = true;
+ msg_puts_attr_len(msgbuf, (ptrdiff_t)len, attr);
+}
+
/*
* The display part of msg_puts_attr_len().
* May be called recursively to display scroll-back text.
diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c
index 9e3025cf89..78627f8703 100644
--- a/src/nvim/os/fs.c
+++ b/src/nvim/os/fs.c
@@ -605,8 +605,11 @@ int os_fsync(int fd)
///
/// @return libuv return code.
static int os_stat(const char *name, uv_stat_t *statbuf)
- FUNC_ATTR_NONNULL_ALL
+ FUNC_ATTR_NONNULL_ARG(2)
{
+ if (!name) {
+ return UV_ENOENT;
+ }
uv_fs_t request;
int result = uv_fs_stat(&fs_loop, &request, name, NULL);
*statbuf = request.statbuf;
@@ -618,7 +621,6 @@ static int os_stat(const char *name, uv_stat_t *statbuf)
///
/// @return libuv error code on error.
int32_t os_getperm(const char *name)
- FUNC_ATTR_NONNULL_ALL
{
uv_stat_t statbuf;
int stat_result = os_stat(name, &statbuf);
@@ -657,7 +659,6 @@ int os_fchown(int fd, uv_uid_t owner, uv_gid_t group)
///
/// @return `true` if `path` exists
bool os_path_exists(const char_u *path)
- FUNC_ATTR_NONNULL_ALL
{
uv_stat_t statbuf;
return os_stat((char *)path, &statbuf) == kLibuvSuccess;
@@ -847,7 +848,7 @@ int os_remove(const char *path)
/// @param[out] file_info Pointer to a FileInfo to put the information in.
/// @return `true` on success, `false` for failure.
bool os_fileinfo(const char *path, FileInfo *file_info)
- FUNC_ATTR_NONNULL_ALL
+ FUNC_ATTR_NONNULL_ARG(2)
{
return os_stat(path, &(file_info->stat)) == kLibuvSuccess;
}
@@ -858,8 +859,11 @@ bool os_fileinfo(const char *path, FileInfo *file_info)
/// @param[out] file_info Pointer to a FileInfo to put the information in.
/// @return `true` on success, `false` for failure.
bool os_fileinfo_link(const char *path, FileInfo *file_info)
- FUNC_ATTR_NONNULL_ALL
+ FUNC_ATTR_NONNULL_ARG(2)
{
+ if (path == NULL) {
+ return false;
+ }
uv_fs_t request;
int result = uv_fs_lstat(&fs_loop, &request, path, NULL);
file_info->stat = request.statbuf;
diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c
index 5d708febea..93ba9ce097 100644
--- a/src/nvim/regexp_nfa.c
+++ b/src/nvim/regexp_nfa.c
@@ -56,13 +56,13 @@ enum {
NFA_RANGE_MIN, /* low end of a range */
NFA_RANGE_MAX, /* high end of a range */
- NFA_CONCAT, /* concatenate two previous items (postfix
- * only) */
- NFA_OR, /* \| (postfix only) */
- NFA_STAR, /* greedy * (posfix only) */
- NFA_STAR_NONGREEDY, /* non-greedy * (postfix only) */
- NFA_QUEST, /* greedy \? (postfix only) */
- NFA_QUEST_NONGREEDY, /* non-greedy \? (postfix only) */
+ NFA_CONCAT, // concatenate two previous items (postfix
+ // only)
+ NFA_OR, // \| (postfix only)
+ NFA_STAR, // greedy * (postfix only)
+ NFA_STAR_NONGREEDY, // non-greedy * (postfix only)
+ NFA_QUEST, // greedy \? (postfix only)
+ NFA_QUEST_NONGREEDY, // non-greedy \? (postfix only)
NFA_BOL, /* ^ Begin line */
NFA_EOL, /* $ End line */
@@ -1988,7 +1988,7 @@ static int nfa_regpiece(void)
// The engine is very inefficient (uses too many states) when the maximum
// is much larger than the minimum and when the maximum is large. Bail out
// if we can use the other engine.
- if ((nfa_re_flags & RE_AUTO) && (maxval > minval + 200 || maxval > 500)) {
+ if ((nfa_re_flags & RE_AUTO) && (maxval > 500 || maxval > minval + 200)) {
return FAIL;
}
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index cd4f4de40f..95973354bc 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -1924,10 +1924,15 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T
if (fill_fold >= 0x80) {
ScreenLinesUC[off + col] = fill_fold;
ScreenLinesC[0][off + col] = 0;
- } else
+ ScreenLines[off + col] = 0x80; // avoid storing zero
+ } else {
ScreenLinesUC[off + col] = 0;
+ ScreenLines[off + col] = fill_fold;
+ }
+ col++;
+ } else {
+ ScreenLines[off + col++] = fill_fold;
}
- ScreenLines[off + col++] = fill_fold;
}
if (text != buf)
@@ -2112,16 +2117,16 @@ win_line (
bool nochange /* not updating for changed text */
)
{
- int col; /* visual column on screen */
- unsigned off; /* offset in ScreenLines/ScreenAttrs */
- int c = 0; /* init for GCC */
- long vcol = 0; /* virtual column (for tabs) */
+ int col = 0; // visual column on screen
+ unsigned off; // offset in ScreenLines/ScreenAttrs
+ int c = 0; // init for GCC
+ long vcol = 0; // virtual column (for tabs)
long vcol_sbr = -1; // virtual column after showbreak
- long vcol_prev = -1; /* "vcol" of previous character */
- char_u *line; /* current line */
- char_u *ptr; /* current position in "line" */
- int row; /* row in the window, excl w_winrow */
- int screen_row; /* row on the screen, incl w_winrow */
+ long vcol_prev = -1; // "vcol" of previous character
+ char_u *line; // current line
+ char_u *ptr; // current position in "line"
+ int row; // row in the window, excl w_winrow
+ int screen_row; // row on the screen, incl w_winrow
char_u extra[18]; /* line number and 'fdc' must fit in here */
int n_extra = 0; /* number of extra chars */
@@ -2195,7 +2200,7 @@ win_line (
int change_start = MAXCOL; /* first col of changed area */
int change_end = -1; /* last col of changed area */
colnr_T trailcol = MAXCOL; /* start of trailing spaces */
- int need_showbreak = FALSE;
+ int need_showbreak = false; // overlong line, skip first x chars
int line_attr = 0; /* attribute for the whole line */
matchitem_T *cur; /* points to the match list */
match_T *shl; /* points to search_hl or a match */
@@ -2522,7 +2527,11 @@ win_line (
if (vcol > v) {
vcol -= c;
ptr = prev_ptr;
- n_skip = v - vcol;
+ // If the character fits on the screen, don't need to skip it.
+ // Except for a TAB.
+ if (((*mb_ptr2cells)(ptr) >= c || *ptr == TAB) && col == 0) {
+ n_skip = v - vcol;
+ }
}
/*
@@ -2705,11 +2714,14 @@ win_line (
draw_state = WL_FOLD;
if (fdc > 0) {
- // Draw the 'foldcolumn'.
- fill_foldcolumn(extra, wp, false, lnum);
+ // Draw the 'foldcolumn'. Allocate a buffer, "extra" may
+ // already be in use.
+ xfree(p_extra_free);
+ p_extra_free = xmalloc(12 + 1);
+ fill_foldcolumn(p_extra_free, wp, false, lnum);
n_extra = fdc;
- p_extra = extra;
- p_extra[n_extra] = NUL;
+ p_extra_free[n_extra] = NUL;
+ p_extra = p_extra_free;
c_extra = NUL;
char_attr = win_hl_attr(wp, HLF_FC);
}
@@ -2805,8 +2817,10 @@ win_line (
// draw 'breakindent': indent wrapped text accodringly
if (draw_state == WL_BRI - 1 && n_extra == 0) {
draw_state = WL_BRI;
- if (wp->w_p_bri && row != startrow && filler_lines == 0) {
- char_attr = wp->w_hl_attr_normal; // was: hl_attr(HLF_AT);
+ // if need_showbreak is set, breakindent also applies
+ if (wp->w_p_bri && (row != startrow || need_showbreak)
+ && filler_lines == 0) {
+ char_attr = wp->w_hl_attr_normal;
if (diff_hlf != (hlf_T)0) {
char_attr = win_hl_attr(wp, diff_hlf);
@@ -3510,6 +3524,7 @@ win_line (
p = xmalloc(len + 1);
memset(p, ' ', len);
p[len] = NUL;
+ xfree(p_extra_free);
p_extra_free = p;
for (i = 0; i < tab_len; i++) {
mb_char2bytes(lcs_tab2, p);
@@ -3625,6 +3640,7 @@ win_line (
memset(p, ' ', n_extra);
STRNCPY(p, p_extra + 1, STRLEN(p_extra) - 1);
p[n_extra] = NUL;
+ xfree(p_extra_free);
p_extra_free = p_extra = p;
} else {
n_extra = byte2cells(c) - 1;
@@ -4298,6 +4314,7 @@ win_line (
cap_col = 0;
}
+ xfree(p_extra_free);
return row;
}
@@ -7423,13 +7440,7 @@ int number_width(win_T *wp)
return n;
}
-/*
- * Set size of the Vim shell.
- * If 'mustset' is TRUE, we must set Rows and Columns, do not get the real
- * window size (this is used for the :win command).
- * If 'mustset' is FALSE, we may try to get the real window size and if
- * it fails use 'width' and 'height'.
- */
+/// Set dimensions of the Nvim application "shell".
void screen_resize(int width, int height)
{
static int busy = FALSE;
@@ -7514,8 +7525,8 @@ void screen_resize(int width, int height)
--busy;
}
-// Check if the new shell size is valid, correct it if it's too small or way
-// too big.
+/// Check if the new Nvim application "shell" dimensions are valid.
+/// Correct it if it's too small or way too big.
void check_shellsize(void)
{
if (Rows < min_rows()) {
diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile
index 7f1e25900b..96de7224c5 100644
--- a/src/nvim/testdir/Makefile
+++ b/src/nvim/testdir/Makefile
@@ -207,7 +207,7 @@ nolog:
# New style of tests uses Vim script with assert calls. These are easier
# to write and a lot easier to read and debug.
# Limitation: Only works with the +eval feature.
-RUN_VIMTEST = VIMRUNTIME=$(SCRIPTSOURCE); export VIMRUNTIME; $(VALGRIND) $(NVIM_PRG) -u unix.vim -U NONE --headless --noplugin
+RUN_VIMTEST = VIMRUNTIME=$(SCRIPTSOURCE); export VIMRUNTIME; $(TOOL) $(NVIM_PRG) -u unix.vim -U NONE --headless --noplugin
newtests: newtestssilent
@/bin/sh -c "if test -f messages && grep -q 'FAILED' messages; then \
diff --git a/src/nvim/testdir/test_breakindent.vim b/src/nvim/testdir/test_breakindent.vim
new file mode 100644
index 0000000000..7deffbe452
--- /dev/null
+++ b/src/nvim/testdir/test_breakindent.vim
@@ -0,0 +1,298 @@
+" Test for breakindent
+"
+" Note: if you get strange failures when adding new tests, it might be that
+" while the test is run, the breakindent cacheing gets in its way.
+" It helps to change the tabstop setting and force a redraw (e.g. see
+" Test_breakindent08())
+if !exists('+breakindent')
+ finish
+endif
+
+source view_util.vim
+
+let s:input ="\tabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP"
+
+function s:screen_lines(lnum, width) abort
+ return ScreenLines([a:lnum, a:lnum + 2], a:width)
+endfunction
+
+function! s:compare_lines(expect, actual)
+ call assert_equal(join(a:expect, "\n"), join(a:actual, "\n"))
+endfunction
+
+function s:test_windows(...)
+ call NewWindow(10, 20)
+ setl ts=4 sw=4 sts=4 breakindent
+ put =s:input
+ exe get(a:000, 0, '')
+endfunction
+
+function s:close_windows(...)
+ call CloseWindow()
+ exe get(a:000, 0, '')
+endfunction
+
+function Test_breakindent01()
+ " simple breakindent test
+ call s:test_windows('setl briopt=min:0')
+ let lines=s:screen_lines(line('.'),8)
+ let expect=[
+\ " abcd",
+\ " qrst",
+\ " GHIJ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunction
+
+function Test_breakindent02()
+ " simple breakindent test with showbreak set
+ call s:test_windows('setl briopt=min:0 sbr=>>')
+ let lines=s:screen_lines(line('.'),8)
+ let expect=[
+\ " abcd",
+\ " >>qr",
+\ " >>EF",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows('set sbr=')
+endfunction
+
+function Test_breakindent03()
+ " simple breakindent test with showbreak set and briopt including sbr
+ call s:test_windows('setl briopt=sbr,min:0 sbr=++')
+ let lines=s:screen_lines(line('.'),8)
+ let expect=[
+\ " abcd",
+\ "++ qrst",
+\ "++ GHIJ",
+\ ]
+ call s:compare_lines(expect, lines)
+ " clean up
+ call s:close_windows('set sbr=')
+endfunction
+
+function Test_breakindent04()
+ " breakindent set with min width 18
+ call s:test_windows('setl sbr= briopt=min:18')
+ let lines=s:screen_lines(line('.'),8)
+ let expect=[
+\ " abcd",
+\ " qrstuv",
+\ " IJKLMN",
+\ ]
+ call s:compare_lines(expect, lines)
+ " clean up
+ call s:close_windows('set sbr=')
+endfunction
+
+function Test_breakindent05()
+ " breakindent set and shift by 2
+ call s:test_windows('setl briopt=shift:2,min:0')
+ let lines=s:screen_lines(line('.'),8)
+ let expect=[
+\ " abcd",
+\ " qr",
+\ " EF",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunction
+
+function Test_breakindent06()
+ " breakindent set and shift by -1
+ call s:test_windows('setl briopt=shift:-1,min:0')
+ let lines=s:screen_lines(line('.'),8)
+ let expect=[
+\ " abcd",
+\ " qrstu",
+\ " HIJKL",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunction
+
+function Test_breakindent07()
+ " breakindent set and shift by 1, Number set sbr=? and briopt:sbr
+ call s:test_windows('setl briopt=shift:1,sbr,min:0 nu sbr=? nuw=4 cpo+=n')
+ let lines=s:screen_lines(line('.'),10)
+ let expect=[
+\ " 2 ab",
+\ "? m",
+\ "? x",
+\ ]
+ call s:compare_lines(expect, lines)
+ " clean up
+ call s:close_windows('set sbr= cpo-=n')
+endfunction
+
+function Test_breakindent07a()
+ " breakindent set and shift by 1, Number set sbr=? and briopt:sbr
+ call s:test_windows('setl briopt=shift:1,sbr,min:0 nu sbr=? nuw=4')
+ let lines=s:screen_lines(line('.'),10)
+ let expect=[
+\ " 2 ab",
+\ " ? m",
+\ " ? x",
+\ ]
+ call s:compare_lines(expect, lines)
+ " clean up
+ call s:close_windows('set sbr=')
+endfunction
+
+function Test_breakindent08()
+ " breakindent set and shift by 1, Number and list set sbr=# and briopt:sbr
+ call s:test_windows('setl briopt=shift:1,sbr,min:0 nu nuw=4 sbr=# list cpo+=n ts=4')
+ " make sure, cache is invalidated!
+ set ts=8
+ redraw!
+ set ts=4
+ redraw!
+ let lines=s:screen_lines(line('.'),10)
+ let expect=[
+\ " 2 ^Iabcd",
+\ "# opq",
+\ "# BCD",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows('set sbr= cpo-=n')
+endfunction
+
+function Test_breakindent08a()
+ " breakindent set and shift by 1, Number and list set sbr=# and briopt:sbr
+ call s:test_windows('setl briopt=shift:1,sbr,min:0 nu nuw=4 sbr=# list')
+ let lines=s:screen_lines(line('.'),10)
+ let expect=[
+\ " 2 ^Iabcd",
+\ " # opq",
+\ " # BCD",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows('set sbr=')
+endfunction
+
+function Test_breakindent09()
+ " breakindent set and shift by 1, Number and list set sbr=#
+ call s:test_windows('setl briopt=shift:1,min:0 nu nuw=4 sbr=# list')
+ let lines=s:screen_lines(line('.'),10)
+ let expect=[
+\ " 2 ^Iabcd",
+\ " #op",
+\ " #AB",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows('set sbr=')
+endfunction
+
+function Test_breakindent10()
+ " breakindent set, Number set sbr=~
+ call s:test_windows('setl cpo+=n sbr=~ nu nuw=4 nolist briopt=sbr,min:0')
+ " make sure, cache is invalidated!
+ set ts=8
+ redraw!
+ set ts=4
+ redraw!
+ let lines=s:screen_lines(line('.'),10)
+ let expect=[
+\ " 2 ab",
+\ "~ mn",
+\ "~ yz",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows('set sbr= cpo-=n')
+endfunction
+
+function Test_breakindent11()
+ " test strdisplaywidth()
+ call s:test_windows('setl cpo-=n sbr=>> nu nuw=4 nolist briopt= ts=4')
+ let text=getline(2)
+ let width = strlen(text[1:])+indent(2)+strlen(&sbr)*3 " text wraps 3 times
+ call assert_equal(width, strdisplaywidth(text))
+ call s:close_windows('set sbr=')
+endfunction
+
+function Test_breakindent12()
+ " test breakindent with long indent
+ let s:input="\t\t\t\t\t{"
+ call s:test_windows('setl breakindent linebreak briopt=min:10 nu numberwidth=3 ts=4 list listchars=tab:>-')
+ let lines=s:screen_lines(2,16)
+ let expect=[
+\ " 2 >--->--->--->",
+\ " ---{ ",
+\ "~ ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows('set nuw=4 listchars=')
+endfunction
+
+function Test_breakindent13()
+ let s:input=""
+ call s:test_windows('setl breakindent briopt=min:10 ts=8')
+ vert resize 20
+ call setline(1, [" a\tb\tc\td\te", " z y x w v"])
+ 1
+ norm! fbgj"ayl
+ 2
+ norm! fygj"byl
+ call assert_equal('d', @a)
+ call assert_equal('w', @b)
+ call s:close_windows()
+endfunction
+
+function Test_breakindent14()
+ let s:input=""
+ call s:test_windows('setl breakindent briopt= ts=8')
+ vert resize 30
+ norm! 3a1234567890
+ norm! a abcde
+ exec "norm! 0\<C-V>tex"
+ let lines=s:screen_lines(line('.'),8)
+ let expect=[
+\ "e ",
+\ "~ ",
+\ "~ ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunction
+
+function Test_breakindent15()
+ let s:input=""
+ call s:test_windows('setl breakindent briopt= ts=8 sw=8')
+ vert resize 30
+ norm! 4a1234567890
+ exe "normal! >>\<C-V>3f0x"
+ let lines=s:screen_lines(line('.'),20)
+ let expect=[
+\ " 1234567890 ",
+\ "~ ",
+\ "~ ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunction
+
+function Test_breakindent16()
+ " Check that overlong lines are indented correctly.
+ let s:input=""
+ call s:test_windows('setl breakindent briopt=min:0 ts=4')
+ call setline(1, "\t".repeat("1234567890", 10))
+ resize 6
+ norm! 1gg$
+ redraw!
+ let lines=s:screen_lines(1,10)
+ let expect=[
+\ " 789012",
+\ " 345678",
+\ " 901234",
+\ ]
+ call s:compare_lines(expect, lines)
+ let lines=s:screen_lines(4,10)
+ let expect=[
+\ " 567890",
+\ " 123456",
+\ " 7890 ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunction
diff --git a/src/nvim/testdir/test_display.vim b/src/nvim/testdir/test_display.vim
new file mode 100644
index 0000000000..0ed672d577
--- /dev/null
+++ b/src/nvim/testdir/test_display.vim
@@ -0,0 +1,71 @@
+" Test for displaying stuff
+
+" Nvim: `:set term` is not supported.
+" if !has('gui_running') && has('unix')
+" set term=ansi
+" endif
+
+source view_util.vim
+
+func! Test_display_foldcolumn()
+ if !has("folding")
+ return
+ endif
+ new
+ vnew
+ vert resize 25
+ call assert_equal(25, winwidth(winnr()))
+ set isprint=@
+
+ 1put='e more noise blah blah‚ more stuff here'
+
+ let expect = [
+ \ "e more noise blah blah<82",
+ \ "> more stuff here "
+ \ ]
+
+ call cursor(2, 1)
+ norm! zt
+ let lines=ScreenLines([1,2], winwidth(0))
+ call assert_equal(expect, lines)
+ set fdc=2
+ let lines=ScreenLines([1,2], winwidth(0))
+ let expect = [
+ \ " e more noise blah blah<",
+ \ " 82> more stuff here "
+ \ ]
+ call assert_equal(expect, lines)
+
+ quit!
+ quit!
+endfunc
+
+func! Test_display_foldtext_mbyte()
+ if !has("folding") || !has("multi_byte")
+ return
+ endif
+ call NewWindow(10, 40)
+ call append(0, range(1,20))
+ exe "set foldmethod=manual foldtext=foldtext() fillchars=fold:\u2500,vert:\u2502 fdc=2"
+ call cursor(2, 1)
+ norm! zf13G
+ let lines=ScreenLines([1,3], winwidth(0)+1)
+ let expect=[
+ \ " 1 \u2502",
+ \ "+ +-- 12 lines: 2". repeat("\u2500", 23). "\u2502",
+ \ " 14 \u2502",
+ \ ]
+ call assert_equal(expect, lines)
+
+ set fillchars=fold:-,vert:\|
+ let lines=ScreenLines([1,3], winwidth(0)+1)
+ let expect=[
+ \ " 1 |",
+ \ "+ +-- 12 lines: 2". repeat("-", 23). "|",
+ \ " 14 |",
+ \ ]
+ call assert_equal(expect, lines)
+
+ set foldtext& fillchars& foldmethod& fdc&
+ bw!
+endfunc
diff --git a/src/nvim/testdir/test_listlbr.vim b/src/nvim/testdir/test_listlbr.vim
new file mode 100644
index 0000000000..7856ee82ab
--- /dev/null
+++ b/src/nvim/testdir/test_listlbr.vim
@@ -0,0 +1,235 @@
+" Test for linebreak and list option (non-utf8)
+
+set encoding=latin1
+scriptencoding latin1
+
+if !exists("+linebreak") || !has("conceal")
+ finish
+endif
+
+source view_util.vim
+
+function s:screen_lines(lnum, width) abort
+ return ScreenLines(a:lnum, a:width)
+endfunction
+
+function! s:compare_lines(expect, actual)
+ call assert_equal(join(a:expect, "\n"), join(a:actual, "\n"))
+endfunction
+
+function s:test_windows(...)
+ call NewWindow(10, 20)
+ setl ts=8 sw=4 sts=4 linebreak sbr= wrap
+ exe get(a:000, 0, '')
+endfunction
+
+function s:close_windows(...)
+ call CloseWindow()
+ exe get(a:000, 0, '')
+endfunction
+
+func Test_set_linebreak()
+ call s:test_windows('setl ts=4 sbr=+')
+ call setline(1, "\tabcdef hijklmn\tpqrstuvwxyz_1060ABCDEFGHIJKLMNOP ")
+ let lines = s:screen_lines([1, 4], winwidth(0))
+ let expect = [
+\ " abcdef ",
+\ "+hijklmn ",
+\ "+pqrstuvwxyz_1060ABC",
+\ "+DEFGHIJKLMNOP ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_linebreak_with_list()
+ call s:test_windows('setl ts=4 sbr=+ list listchars=')
+ call setline(1, "\tabcdef hijklmn\tpqrstuvwxyz_1060ABCDEFGHIJKLMNOP ")
+ let lines = s:screen_lines([1, 4], winwidth(0))
+ let expect = [
+\ "^Iabcdef hijklmn^I ",
+\ "+pqrstuvwxyz_1060ABC",
+\ "+DEFGHIJKLMNOP ",
+\ "~ ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_linebreak_with_nolist()
+ call s:test_windows('setl ts=4 sbr=+ nolist')
+ call setline(1, "\tabcdef hijklmn\tpqrstuvwxyz_1060ABCDEFGHIJKLMNOP ")
+ let lines = s:screen_lines([1, 4], winwidth(0))
+ let expect = [
+\ " abcdef ",
+\ "+hijklmn ",
+\ "+pqrstuvwxyz_1060ABC",
+\ "+DEFGHIJKLMNOP ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_should_break()
+ call s:test_windows('setl sbr=+ nolist')
+ call setline(1, "1\t" . repeat('a', winwidth(0)-2))
+ let lines = s:screen_lines([1, 4], winwidth(0))
+ let expect = [
+\ "1 ",
+\ "+aaaaaaaaaaaaaaaaaa ",
+\ "~ ",
+\ "~ ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_linebreak_with_conceal()
+ call s:test_windows('setl cpo&vim sbr=+ list conceallevel=2 concealcursor=nv listchars=tab:ab')
+ call setline(1, "_S_\t bla")
+ syn match ConcealVar contained /_/ conceal
+ syn match All /.*/ contains=ConcealVar
+ let lines = s:screen_lines([1, 4], winwidth(0))
+ let expect = [
+\ "Sabbbbbb bla ",
+\ "~ ",
+\ "~ ",
+\ "~ ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_virtual_block()
+ call s:test_windows('setl sbr=+')
+ call setline(1, [
+\ "REMOVE: this not",
+\ "REMOVE: aaaaaaaaaaaaa",
+\ ])
+ exe "norm! 1/^REMOVE:"
+ exe "norm! 0\<C-V>jf x"
+ $put
+ let lines = s:screen_lines([1, 4], winwidth(0))
+ let expect = [
+\ "this not ",
+\ "aaaaaaaaaaaaa ",
+\ "REMOVE: ",
+\ "REMOVE: ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_virtual_block_and_vbA()
+ call s:test_windows()
+ call setline(1, "long line: " . repeat("foobar ", 40) . "TARGET at end")
+ exe "norm! $3B\<C-v>eAx\<Esc>"
+ let lines = s:screen_lines([1, 10], winwidth(0))
+ let expect = [
+\ "foobar foobar ",
+\ "foobar foobar ",
+\ "foobar foobar ",
+\ "foobar foobar ",
+\ "foobar foobar ",
+\ "foobar foobar ",
+\ "foobar foobar ",
+\ "foobar foobar ",
+\ "foobar foobar ",
+\ "foobar TARGETx at ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_virtual_char_and_block()
+ call s:test_windows()
+ call setline(1, "1111-1111-1111-11-1111-1111-1111")
+ exe "norm! 0f-lv3lc2222\<Esc>bgj."
+ let lines = s:screen_lines([1, 2], winwidth(0))
+ let expect = [
+\ "1111-2222-1111-11- ",
+\ "1111-2222-1111 ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_undo_after_block_visual()
+ call s:test_windows()
+ call setline(1, ["aaa", "aaa", "a"])
+ exe "norm! gg\<C-V>2j~e."
+ let lines = s:screen_lines([1, 3], winwidth(0))
+ let expect = [
+\ "AaA ",
+\ "AaA ",
+\ "A ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_norm_after_block_visual()
+ call s:test_windows()
+ call setline(1, ["abcd{ef", "ghijklm", "no}pgrs"])
+ exe "norm! ggf{\<C-V>\<C-V>c%"
+ let lines = s:screen_lines([1, 3], winwidth(0))
+ let expect = [
+\ "abcdpgrs ",
+\ "~ ",
+\ "~ ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_block_replace_after_wrapping()
+ call s:test_windows()
+ call setline(1, repeat("a", 150))
+ exe "norm! 0yypk147|\<C-V>jr0"
+ call assert_equal(repeat("a", 146) . "0aaa", getline(1))
+ call assert_equal(repeat("a", 146) . "0aaa", getline(2))
+ let lines = s:screen_lines([1, 10], winwidth(0))
+ let expect = [
+\ "aaaaaaaaaaaaaaaaaaaa",
+\ "aaaaaaaaaaaaaaaaaaaa",
+\ "aaaaaaaaaaaaaaaaaaaa",
+\ "aaaaaaaaaaaaaaaaaaaa",
+\ "aaaaaaaaaaaaaaaaaaaa",
+\ "aaaaaaaaaaaaaaaaaaaa",
+\ "aaaaaaaaaaaaaaaaaaaa",
+\ "aaaaaa0aaa ",
+\ "@ ",
+\ "@ ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_list_with_listchars()
+ call s:test_windows('setl list listchars=space:_,trail:-,tab:>-,eol:$')
+ call setline(1, "a aaaaaaaaaaaaaaaaaaaaaa\ta ")
+ let lines = s:screen_lines([1, 3], winwidth(0))
+ let expect = [
+\ "a_ ",
+\ "aaaaaaaaaaaaaaaaaaaa",
+\ "aa>-----a-$ ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_list_with_tab_and_skipping_first_chars()
+ call s:test_windows('setl list listchars=tab:>- ts=70 nowrap')
+ call setline(1, ["iiiiiiiiiiiiiiii\taaaaaaaaaaaaaaaaaa", "iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii\taaaaaaaaaaaaaaaaaa", "iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii\taaaaaaaaaaaaaaaaaa", "iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii\taaaaaaaaaaaaaaaaaa"])
+ call cursor(4,64)
+ norm! 2zl
+ let lines = s:screen_lines([1, 4], winwidth(0))
+ let expect = [
+\ "---------------aaaaa",
+\ "---------------aaaaa",
+\ "---------------aaaaa",
+\ "iiiiiiiii>-----aaaaa",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfu
diff --git a/src/nvim/testdir/test_listlbr_utf8.vim b/src/nvim/testdir/test_listlbr_utf8.vim
new file mode 100644
index 0000000000..980d67d49d
--- /dev/null
+++ b/src/nvim/testdir/test_listlbr_utf8.vim
@@ -0,0 +1,229 @@
+" Test for linebreak and list option in utf-8 mode
+
+set encoding=utf-8
+scriptencoding utf-8
+
+if !exists("+linebreak") || !has("conceal") || !has("signs")
+ finish
+endif
+
+source view_util.vim
+
+function s:screen_lines(lnum, width) abort
+ return ScreenLines(a:lnum, a:width)
+endfunction
+
+function! s:compare_lines(expect, actual)
+ call assert_equal(a:expect, a:actual)
+endfunction
+
+function s:screen_attr(lnum, chars, ...) abort
+ let line = getline(a:lnum)
+ let attr = []
+ let prefix = get(a:000, 0, 0)
+ for i in range(a:chars[0], a:chars[1])
+ let scol = strdisplaywidth(strcharpart(line, 0, i-1)) + 1
+ let attr += [screenattr(a:lnum, scol + prefix)]
+ endfor
+ return attr
+endfunction
+
+function s:test_windows(...)
+ call NewWindow(10, 20)
+ setl ts=4 sw=4 sts=4 linebreak sbr=+ wrap
+ exe get(a:000, 0, '')
+endfunction
+
+function s:close_windows(...)
+ call CloseWindow()
+ exe get(a:000, 0, '')
+endfunction
+
+func Test_linebreak_with_fancy_listchars()
+ call s:test_windows("setl list listchars=nbsp:\u2423,tab:\u2595\u2014,trail:\u02d1,eol:\ub6")
+ call setline(1, "\tabcdef hijklmn\tpqrstuvwxyz\u00a01060ABCDEFGHIJKLMNOP ")
+ redraw!
+ let lines = s:screen_lines([1, 4], winwidth(0))
+ let expect = [
+\ "▕———abcdef ",
+\ "+hijklmn▕——— ",
+\ "+pqrstuvwxyz␣1060ABC",
+\ "+DEFGHIJKLMNOPˑ¶ ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_nolinebreak_with_list()
+ call s:test_windows("setl nolinebreak list listchars=nbsp:\u2423,tab:\u2595\u2014,trail:\u02d1,eol:\ub6")
+ call setline(1, "\tabcdef hijklmn\tpqrstuvwxyz\u00a01060ABCDEFGHIJKLMNOP ")
+ redraw!
+ let lines = s:screen_lines([1, 4], winwidth(0))
+ let expect = [
+\ "▕———abcdef hijklmn▕—",
+\ "+pqrstuvwxyz␣1060ABC",
+\ "+DEFGHIJKLMNOPˑ¶ ",
+\ "~ ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_linebreak_with_nolist()
+ call s:test_windows('setl nolist')
+ call setline(1, "\t*mask = nil;")
+ redraw!
+ let lines = s:screen_lines([1, 4], winwidth(0))
+ let expect = [
+\ " *mask = nil; ",
+\ "~ ",
+\ "~ ",
+\ "~ ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_list_and_concealing1()
+ call s:test_windows('setl list listchars=tab:>- cole=1')
+ call setline(1, [
+\ "#define ABCDE\t\t1",
+\ "#define ABCDEF\t\t1",
+\ "#define ABCDEFG\t\t1",
+\ "#define ABCDEFGH\t1",
+\ "#define MSG_MODE_FILE\t\t\t1",
+\ "#define MSG_MODE_CONSOLE\t\t2",
+\ "#define MSG_MODE_FILE_AND_CONSOLE\t3",
+\ "#define MSG_MODE_FILE_THEN_CONSOLE\t4",
+\ ])
+ vert resize 40
+ syn match Conceal conceal cchar=>'AB\|MSG_MODE'
+ redraw!
+ let lines = s:screen_lines([1, 7], winwidth(0))
+ let expect = [
+\ "#define ABCDE>-->---1 ",
+\ "#define >CDEF>-->---1 ",
+\ "#define >CDEFG>->---1 ",
+\ "#define >CDEFGH>----1 ",
+\ "#define >_FILE>--------->--->---1 ",
+\ "#define >_CONSOLE>---------->---2 ",
+\ "#define >_FILE_AND_CONSOLE>---------3 ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_list_and_concealing2()
+ call s:test_windows('setl nowrap ts=2 list listchars=tab:>- cole=2 concealcursor=n')
+ call setline(1, "bbeeeeee\t\t;\tsome text")
+ vert resize 40
+ syn clear
+ syn match meaning /;\s*\zs.*/
+ syn match hasword /^\x\{8}/ contains=word
+ syn match word /\<\x\{8}\>/ contains=beginword,endword contained
+ syn match beginword /\<\x\x/ contained conceal
+ syn match endword /\x\{6}\>/ contained
+ hi meaning guibg=blue
+ hi beginword guibg=green
+ hi endword guibg=red
+ redraw!
+ let lines = s:screen_lines([1, 1], winwidth(0))
+ let expect = [
+\ "eeeeee>--->-;>some text ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_screenattr_for_comment()
+ call s:test_windows("setl ft=c ts=7 list listchars=nbsp:\u2423,tab:\u2595\u2014,trail:\u02d1,eol:\ub6")
+ call setline(1, " /*\t\t and some more */")
+ norm! gg0
+ syntax on
+ hi SpecialKey term=underline ctermfg=red guifg=red
+ redraw!
+ let line = getline(1)
+ let attr = s:screen_attr(1, [1, 6])
+ call assert_notequal(attr[0], attr[1])
+ call assert_notequal(attr[1], attr[3])
+ call assert_notequal(attr[3], attr[5])
+ call s:close_windows()
+endfunc
+
+func Test_visual_block_and_selection_exclusive()
+ call s:test_windows('setl selection=exclusive')
+ call setline(1, "long line: " . repeat("foobar ", 40) . "TARGETÃ' at end")
+ exe "norm! $3B\<C-v>eAx\<Esc>"
+ let lines = s:screen_lines([1, 10], winwidth(0))
+ let expect = [
+\ "+foobar foobar ",
+\ "+foobar foobar ",
+\ "+foobar foobar ",
+\ "+foobar foobar ",
+\ "+foobar foobar ",
+\ "+foobar foobar ",
+\ "+foobar foobar ",
+\ "+foobar foobar ",
+\ "+foobar foobar ",
+\ "+foobar TARGETÃx' ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_multibyte_sign_and_colorcolumn()
+ call s:test_windows("setl nolinebreak cc=3 list listchars=nbsp:\u2423,tab:\u2595\u2014,trail:\u02d1,eol:\ub6")
+ call setline(1, ["", "a b c", "a b c"])
+ exe "sign define foo text=\uff0b"
+ exe "sign place 1 name=foo line=2 buffer=" . bufnr('%')
+ redraw!
+ norm! ggj0
+ let signwidth = strdisplaywidth("\uff0b")
+ let attr1 = s:screen_attr(2, [1, 3], signwidth)
+ let attr2 = s:screen_attr(3, [1, 3], signwidth)
+ call assert_equal(attr1[0], attr2[0])
+ call assert_equal(attr1[1], attr2[1])
+ call assert_equal(attr1[2], attr2[2])
+ let lines = s:screen_lines([1, 3], winwidth(0))
+ let expect = [
+\ " ¶ ",
+\ "+a b c¶ ",
+\ " a b c¶ ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_chinese_char_on_wrap_column()
+ call s:test_windows("setl nolbr wrap sbr=")
+ syntax off
+ call setline(1, [
+\ 'aaaaaaaaaaaaaaaaaaa中'.
+\ 'aaaaaaaaaaaaaaaaa中'.
+\ 'aaaaaaaaaaaaaaaaa中'.
+\ 'aaaaaaaaaaaaaaaaa中'.
+\ 'aaaaaaaaaaaaaaaaa中'.
+\ 'aaaaaaaaaaaaaaaaa中'.
+\ 'aaaaaaaaaaaaaaaaa中'.
+\ 'aaaaaaaaaaaaaaaaa中'.
+\ 'aaaaaaaaaaaaaaaaa中'.
+\ 'aaaaaaaaaaaaaaaaa中'.
+\ 'hello'])
+ call cursor(1,1)
+ norm! $
+ redraw!
+ let expect=[
+\ '中aaaaaaaaaaaaaaaaa>',
+\ '中aaaaaaaaaaaaaaaaa>',
+\ '中aaaaaaaaaaaaaaaaa>',
+\ '中aaaaaaaaaaaaaaaaa>',
+\ '中aaaaaaaaaaaaaaaaa>',
+\ '中aaaaaaaaaaaaaaaaa>',
+\ '中aaaaaaaaaaaaaaaaa>',
+\ '中aaaaaaaaaaaaaaaaa>',
+\ '中aaaaaaaaaaaaaaaaa>',
+\ '中hello ']
+ let lines = s:screen_lines([1, 10], winwidth(0))
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfu
diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim
index 188a7ed0f3..9d61921988 100644
--- a/src/nvim/testdir/test_window_cmd.vim
+++ b/src/nvim/testdir/test_window_cmd.vim
@@ -336,6 +336,50 @@ func Test_window_width()
bw Xa Xb Xc
endfunc
+func Test_equalalways_on_close()
+ set equalalways
+ vsplit
+ windo split
+ split
+ wincmd J
+ " now we have a frame top-left with two windows, a frame top-right with two
+ " windows and a frame at the bottom, full-width.
+ let height_1 = winheight(1)
+ let height_2 = winheight(2)
+ let height_3 = winheight(3)
+ let height_4 = winheight(4)
+ " closing the bottom window causes all windows to be resized.
+ close
+ call assert_notequal(height_1, winheight(1))
+ call assert_notequal(height_2, winheight(2))
+ call assert_notequal(height_3, winheight(3))
+ call assert_notequal(height_4, winheight(4))
+ call assert_equal(winheight(1), winheight(3))
+ call assert_equal(winheight(2), winheight(4))
+
+ 1wincmd w
+ split
+ 4wincmd w
+ resize + 5
+ " left column has three windows, equalized heights.
+ " right column has two windows, top one a bit higher
+ let height_1 = winheight(1)
+ let height_2 = winheight(2)
+ let height_4 = winheight(4)
+ let height_5 = winheight(5)
+ 3wincmd w
+ " closing window in left column equalizes heights in left column but not in
+ " the right column
+ close
+ call assert_notequal(height_1, winheight(1))
+ call assert_notequal(height_2, winheight(2))
+ call assert_equal(height_4, winheight(3))
+ call assert_equal(height_5, winheight(4))
+
+ only
+ set equalalways&
+endfunc
+
func Test_window_jump_tag()
help
/iccf
diff --git a/src/nvim/testdir/view_util.vim b/src/nvim/testdir/view_util.vim
new file mode 100644
index 0000000000..eb92630761
--- /dev/null
+++ b/src/nvim/testdir/view_util.vim
@@ -0,0 +1,30 @@
+" Functions about view shared by several tests
+
+" ScreenLines(lnum, width) or
+" ScreenLines([start, end], width)
+function! ScreenLines(lnum, width) abort
+ redraw!
+ if type(a:lnum) == v:t_list
+ let start = a:lnum[0]
+ let end = a:lnum[1]
+ else
+ let start = a:lnum
+ let end = a:lnum
+ endif
+ let lines = []
+ for l in range(start, end)
+ let lines += [join(map(range(1, a:width), 'nr2char(screenchar(l, v:val))'), '')]
+ endfor
+ return lines
+endfunction
+
+function! NewWindow(height, width) abort
+ exe a:height . 'new'
+ exe a:width . 'vsp'
+ redraw!
+endfunction
+
+function! CloseWindow() abort
+ bw!
+ redraw!
+endfunction
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index c29ec09638..b5af5b0333 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -45,7 +45,7 @@
#define OUTBUF_SIZE 0xffff
#define TOO_MANY_EVENTS 1000000
-#define STARTS_WITH(str, prefix) (strlen(term) >= (sizeof(prefix) - 1) \
+#define STARTS_WITH(str, prefix) (strlen(str) >= (sizeof(prefix) - 1) \
&& 0 == memcmp((str), (prefix), sizeof(prefix) - 1))
#define TMUX_WRAP(is_tmux, seq) ((is_tmux) \
? "\x1bPtmux;\x1b" seq "\x1b\\" : seq)
@@ -1256,7 +1256,7 @@ static void patch_terminfo_bugs(TUIData *data, const char *term,
bool teraterm = terminfo_is_term_family(term, "teraterm");
bool putty = terminfo_is_term_family(term, "putty");
bool screen = terminfo_is_term_family(term, "screen");
- bool tmux = terminfo_is_term_family(term, "tmux");
+ bool tmux = terminfo_is_term_family(term, "tmux") || !!os_getenv("TMUX");
bool st = terminfo_is_term_family(term, "st");
bool gnome = terminfo_is_term_family(term, "gnome")
|| terminfo_is_term_family(term, "vte");
@@ -1270,7 +1270,6 @@ static void patch_terminfo_bugs(TUIData *data, const char *term,
bool mate_pretending_xterm = xterm && colorterm
&& strstr(colorterm, "mate-terminal");
bool true_xterm = xterm && !!xterm_version;
- bool tmux_pretending_screen = screen && !!os_getenv("TMUX");
char *fix_normal = (char *)unibi_get_str(ut, unibi_cursor_normal);
if (fix_normal) {
@@ -1347,7 +1346,7 @@ static void patch_terminfo_bugs(TUIData *data, const char *term,
// per the screen manual; 2017-04 terminfo.src lacks these.
unibi_set_if_empty(ut, unibi_to_status_line, "\x1b_");
unibi_set_if_empty(ut, unibi_from_status_line, "\x1b\\");
- } else if (terminfo_is_term_family(term, "tmux")) {
+ } else if (tmux) {
unibi_set_if_empty(ut, unibi_to_status_line, "\x1b_");
unibi_set_if_empty(ut, unibi_from_status_line, "\x1b\\");
} else if (terminfo_is_term_family(term, "interix")) {
@@ -1408,12 +1407,11 @@ static void patch_terminfo_bugs(TUIData *data, const char *term,
unibi_set_str(ut, unibi_set_a_foreground, XTERM_SETAF_256_COLON);
unibi_set_str(ut, unibi_set_a_background, XTERM_SETAB_256_COLON);
} else if (konsole || xterm || gnome || rxvt || st || putty
- || linuxvt // Linux 4.8+ supports 256-colour SGR.
- || mate_pretending_xterm || gnome_pretending_xterm
- || tmux || tmux_pretending_screen
- || (colorterm && strstr(colorterm, "256"))
- || (term && strstr(term, "256"))
- ) {
+ || linuxvt // Linux 4.8+ supports 256-colour SGR.
+ || mate_pretending_xterm || gnome_pretending_xterm
+ || tmux
+ || (colorterm && strstr(colorterm, "256"))
+ || (term && strstr(term, "256"))) {
unibi_set_num(ut, unibi_max_colors, 256);
unibi_set_str(ut, unibi_set_a_foreground, XTERM_SETAF_256);
unibi_set_str(ut, unibi_set_a_background, XTERM_SETAB_256);
@@ -1429,12 +1427,17 @@ static void patch_terminfo_bugs(TUIData *data, const char *term,
}
}
- // Dickey ncurses terminfo has included the Ss and Se capabilities, pioneered
- // by tmux, since 2011-07-14. So adding them to terminal types, that do
- // actually have such control sequences but lack the correct definitions in
- // terminfo, is a fixup, not an augmentation.
- data->unibi_ext.reset_cursor_style = unibi_find_ext_str(ut, "Se");
- data->unibi_ext.set_cursor_style = unibi_find_ext_str(ut, "Ss");
+ // Some terminals can not currently be trusted to report if they support
+ // DECSCUSR or not. So we need to have a blacklist for when we should not
+ // trust the reported features.
+ if (!((vte_version != 0 && vte_version < 3900) || konsole)) {
+ // Dickey ncurses terminfo has included the Ss and Se capabilities,
+ // pioneered by tmux, since 2011-07-14. So adding them to terminal types,
+ // that do actually have such control sequences but lack the correct
+ // definitions in terminfo, is a fixup, not an augmentation.
+ data->unibi_ext.reset_cursor_style = unibi_find_ext_str(ut, "Se");
+ data->unibi_ext.set_cursor_style = unibi_find_ext_str(ut, "Ss");
+ }
if (-1 == data->unibi_ext.set_cursor_style) {
// The DECSCUSR sequence to change the cursor shape is widely
// supported by several terminal types and should be in many
@@ -1442,6 +1445,13 @@ static void patch_terminfo_bugs(TUIData *data, const char *term,
// https://github.com/gnachman/iTerm2/pull/92 for more.
// xterm even has an extended version that has a vertical bar.
if (true_xterm // per xterm ctlseqs doco (since version 282)
+ // per MinTTY 0.4.3-1 release notes from 2009
+ || putty
+ // per https://bugzilla.gnome.org/show_bug.cgi?id=720821
+ || (vte_version >= 3900)
+ || tmux // per tmux manual page
+ // https://lists.gnu.org/archive/html/screen-devel/2013-03/msg00000.html
+ || screen
|| rxvt // per command.C
// per analysis of VT100Terminal.m
|| iterm || iterm_pretending_xterm
@@ -1454,50 +1464,34 @@ static void patch_terminfo_bugs(TUIData *data, const char *term,
(int)unibi_add_ext_str(ut, "Ss", "\x1b[%p1%d q");
if (-1 == data->unibi_ext.reset_cursor_style) {
data->unibi_ext.reset_cursor_style = (int)unibi_add_ext_str(ut, "Se",
- "");
+ "");
}
unibi_set_ext_str(ut, (size_t)data->unibi_ext.reset_cursor_style,
"\x1b[ q");
- } else if (
- // per MinTTY 0.4.3-1 release notes from 2009
- putty
- // per https://bugzilla.gnome.org/show_bug.cgi?id=720821
- || (vte_version >= 3900)
- // per tmux manual page and per
- // https://lists.gnu.org/archive/html/screen-devel/2013-03/msg00000.html
- || screen) {
- // Since we use the xterm extension, we must map it to the unextended form
- data->unibi_ext.set_cursor_style = (int)unibi_add_ext_str(ut, "Ss",
- "\x1b[%?"
- "%p1%{4}%>" "%t%p1%{2}%-" // a bit of a bodge for extension values
- "%e%p1" // the conventional codes are just passed through
- "%;%d q");
- if (-1 == data->unibi_ext.reset_cursor_style) {
- data->unibi_ext.reset_cursor_style = (int)unibi_add_ext_str(ut, "Se",
- "");
- }
- unibi_set_ext_str(ut, (size_t)data->unibi_ext.reset_cursor_style,
- "\x1b[ q");
} else if (linuxvt) {
// Linux uses an idiosyncratic escape code to set the cursor shape and
// does not support DECSCUSR.
+ // See http://linuxgazette.net/137/anonymous.html for more info
data->unibi_ext.set_cursor_style = (int)unibi_add_ext_str(ut, "Ss",
"\x1b[?"
"%?"
// The parameter passed to Ss is the DECSCUSR parameter, so the
// terminal capability has to translate into the Linux idiosyncratic
// parameter.
- "%p1%{2}%<" "%t%{8}" // blink block
- "%p1%{2}%=" "%t%{24}" // steady block
- "%p1%{3}%=" "%t%{1}" // blink underline
- "%p1%{4}%=" "%t%{17}" // steady underline
- "%p1%{5}%=" "%t%{1}" // blink bar
- "%p1%{6}%=" "%t%{17}" // steady bar
- "%e%{0}" // anything else
+ //
+ // linuxvt only supports block and underline. It is also only
+ // possible to have a steady block (no steady underline)
+ "%p1%{2}%<" "%t%{8}" // blink block
+ "%e%p1%{2}%=" "%t%{112}" // steady block
+ "%e%p1%{3}%=" "%t%{4}" // blink underline (set to half block)
+ "%e%p1%{4}%=" "%t%{4}" // steady underline
+ "%e%p1%{5}%=" "%t%{2}" // blink bar (set to underline)
+ "%e%p1%{6}%=" "%t%{2}" // steady bar
+ "%e%{0}" // anything else
"%;" "%dc");
if (-1 == data->unibi_ext.reset_cursor_style) {
data->unibi_ext.reset_cursor_style = (int)unibi_add_ext_str(ut, "Se",
- "");
+ "");
}
unibi_set_ext_str(ut, (size_t)data->unibi_ext.reset_cursor_style,
"\x1b[?c");
@@ -1507,17 +1501,17 @@ static void patch_terminfo_bugs(TUIData *data, const char *term,
// nonce profile, which has side-effects on temporary font resizing.
// In an ideal world, Konsole would just support DECSCUSR.
data->unibi_ext.set_cursor_style = (int)unibi_add_ext_str(ut, "Ss",
- "\x1b]50;CursorShape=%?"
+ TMUX_WRAP(tmux, "\x1b]50;CursorShape=%?"
"%p1%{3}%<" "%t%{0}" // block
- "%e%p1%{4}%<" "%t%{2}" // underline
+ "%e%p1%{5}%<" "%t%{2}" // underline
"%e%{1}" // everything else is bar
"%;%d;BlinkingCursorEnabled=%?"
"%p1%{1}%<" "%t%{1}" // Fortunately if we exclude zero as special,
"%e%p1%{1}%&" // in all other cases we can treat bit #0 as a flag.
- "%;%d\x07");
+ "%;%d\x07"));
if (-1 == data->unibi_ext.reset_cursor_style) {
data->unibi_ext.reset_cursor_style = (int)unibi_add_ext_str(ut, "Se",
- "");
+ "");
}
unibi_set_ext_str(ut, (size_t)data->unibi_ext.reset_cursor_style,
"\x1b]50;\x07");
diff --git a/src/nvim/ui.c b/src/nvim/ui.c
index a60c061949..b85a01814d 100644
--- a/src/nvim/ui.c
+++ b/src/nvim/ui.c
@@ -193,7 +193,12 @@ void ui_refresh(void)
}
row = col = 0;
+
+ int save_p_lz = p_lz;
+ p_lz = false; // convince redrawing() to return true ...
screen_resize(width, height);
+ p_lz = save_p_lz;
+
for (UIWidget i = 0; (int)i < UI_WIDGETS; i++) {
ui_set_external(i, ext_widgets[i]);
}
diff --git a/src/nvim/version.c b/src/nvim/version.c
index bc5035f215..2a31b7d53d 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -273,8 +273,8 @@ static const int included_patches[] = {
// 682,
// 681,
// 680,
- // 679,
- // 678,
+ 679,
+ 678,
// 677,
// 676,
// 675,
@@ -428,13 +428,13 @@ static const int included_patches[] = {
// 527,
// 526,
// 525,
- // 524,
+ 524,
// 523,
// 522,
// 521,
// 520,
// 519,
- // 518,
+ 518,
// 517,
// 516,
// 515,
@@ -558,7 +558,7 @@ static const int included_patches[] = {
// 397,
// 396,
// 395,
- // 394,
+ 394,
393,
// 392,
// 391,
@@ -641,7 +641,7 @@ static const int included_patches[] = {
// 314,
// 313,
// 312,
- // 311,
+ 311,
// 310,
// 309,
// 308,
@@ -662,7 +662,7 @@ static const int included_patches[] = {
// 293,
// 292,
// 291,
- // 290,
+ 290,
// 289,
// 288 NA
// 287,
@@ -717,7 +717,7 @@ static const int included_patches[] = {
// 238,
// 237,
// 236,
- // 235,
+ 235,
// 234,
// 233,
// 232 NA
@@ -787,7 +787,7 @@ static const int included_patches[] = {
168,
167,
// 166,
- // 165,
+ 165,
// 164,
// 163 NA
// 162 NA
@@ -824,9 +824,9 @@ static const int included_patches[] = {
// 131,
// 130 NA
// 129 NA
- // 128,
+ 128,
127,
- // 126,
+ 126,
// 125,
124,
// 123 NA
@@ -862,7 +862,7 @@ static const int included_patches[] = {
// 93 NA
// 92,
// 91,
- // 90,
+ 90,
// 89 NA
88,
// 87 NA
diff --git a/src/nvim/window.c b/src/nvim/window.c
index faf5bceb56..081fc98816 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -1878,6 +1878,7 @@ int win_close(win_T *win, int free_buf)
int dir;
int help_window = FALSE;
tabpage_T *prev_curtab = curtab;
+ frame_T *win_frame = win->w_frame->fr_parent;
if (last_window()) {
EMSG(_("E444: Cannot close last window"));
@@ -2027,7 +2028,9 @@ int win_close(win_T *win, int free_buf)
check_cursor();
}
if (p_ea && (*p_ead == 'b' || *p_ead == dir)) {
- win_equal(curwin, true, dir);
+ // If the frame of the closed window contains the new current window,
+ // only resize that frame. Otherwise resize all windows.
+ win_equal(curwin, curwin->w_frame->fr_parent == win_frame, dir);
} else {
win_comp_pos();
}