mirror of
https://github.com/neovim/neovim.git
synced 2024-12-20 11:15:14 -07:00
Merge #6364 'command-line color hook'
This commit is contained in:
commit
bb70eec177
@ -4703,6 +4703,7 @@ input({opts})
|
|||||||
cancelreturn "" Same as {cancelreturn} from
|
cancelreturn "" Same as {cancelreturn} from
|
||||||
|inputdialog()|. Also works with
|
|inputdialog()|. Also works with
|
||||||
input().
|
input().
|
||||||
|
highlight nothing Highlight handler: |Funcref|.
|
||||||
|
|
||||||
The highlighting set with |:echohl| is used for the prompt.
|
The highlighting set with |:echohl| is used for the prompt.
|
||||||
The input is entered just like a command-line, with the same
|
The input is entered just like a command-line, with the same
|
||||||
@ -4725,7 +4726,35 @@ input({opts})
|
|||||||
"-complete=" argument. Refer to |:command-completion| for
|
"-complete=" argument. Refer to |:command-completion| for
|
||||||
more information. Example: >
|
more information. Example: >
|
||||||
let fname = input("File: ", "", "file")
|
let fname = input("File: ", "", "file")
|
||||||
<
|
< *E5400* *E5402*
|
||||||
|
The optional `highlight` key allows specifying function which
|
||||||
|
will be used for highlighting user input. This function
|
||||||
|
receives user input as its only argument and must return
|
||||||
|
a list of 3-tuples [hl_start_col, hl_end_col + 1, hl_group]
|
||||||
|
where
|
||||||
|
hl_start_col is the first highlighted column,
|
||||||
|
hl_end_col is the last highlighted column (+ 1!),
|
||||||
|
hl_group is |:hl| group used for highlighting.
|
||||||
|
*E5403* *E5404* *E5405* *E5406*
|
||||||
|
Both hl_start_col and hl_end_col + 1 must point to the start
|
||||||
|
of the multibyte character (highlighting must not break
|
||||||
|
multibyte characters), hl_end_col + 1 may be equal to the
|
||||||
|
input length. Start column must be in range [0, len(input)),
|
||||||
|
end column must be in range (hl_start_col, len(input)],
|
||||||
|
sections must be ordered so that next hl_start_col is greater
|
||||||
|
then or equal to previous hl_end_col.
|
||||||
|
|
||||||
|
Highlight function is called at least once for each new
|
||||||
|
displayed input string, before command-line is redrawn. It is
|
||||||
|
expected that function is pure for the duration of one input()
|
||||||
|
call, i.e. it produces the same output for the same input, so
|
||||||
|
output may be memoized. Function is run like under |:silent|
|
||||||
|
modifier. If the function causes any errors, it will be
|
||||||
|
skipped for the duration of the current input() call.
|
||||||
|
|
||||||
|
Currently coloring is disabled when command-line contains
|
||||||
|
arabic characters.
|
||||||
|
|
||||||
NOTE: This function must not be used in a startup file, for
|
NOTE: This function must not be used in a startup file, for
|
||||||
the versions that only run in GUI mode (e.g., the Win32 GUI).
|
the versions that only run in GUI mode (e.g., the Win32 GUI).
|
||||||
Note: When input() is called from within a mapping it will
|
Note: When input() is called from within a mapping it will
|
||||||
|
@ -129,7 +129,6 @@ Commands:
|
|||||||
Functions:
|
Functions:
|
||||||
|dictwatcheradd()| notifies a callback whenever a |Dict| is modified
|
|dictwatcheradd()| notifies a callback whenever a |Dict| is modified
|
||||||
|dictwatcherdel()|
|
|dictwatcherdel()|
|
||||||
|execute()| works with |:redir|
|
|
||||||
|menu_get()|
|
|menu_get()|
|
||||||
|msgpackdump()|, |msgpackparse()| provide msgpack de/serialization
|
|msgpackdump()|, |msgpackparse()| provide msgpack de/serialization
|
||||||
|
|
||||||
@ -147,6 +146,14 @@ Highlight groups:
|
|||||||
|hl-TermCursorNC|
|
|hl-TermCursorNC|
|
||||||
|hl-Whitespace| highlights 'listchars' whitespace
|
|hl-Whitespace| highlights 'listchars' whitespace
|
||||||
|
|
||||||
|
UI:
|
||||||
|
*E5408* *E5409* *g:Nvim_color_expr* *g:Nvim_color_cmdline*
|
||||||
|
Command-line coloring is supported. Only |input()| and |inputdialog()| may
|
||||||
|
be colored. For testing purposes expressions (e.g. |i_CTRL-R_=|) and regular
|
||||||
|
command-line (|:|) are colored by callbacks defined in `g:Nvim_color_expr`
|
||||||
|
and `g:Nvim_color_cmdline` respectively (these callbacks are for testing
|
||||||
|
only, and will be removed in a future version).
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
4. Changed features *nvim-features-changed*
|
4. Changed features *nvim-features-changed*
|
||||||
|
|
||||||
@ -174,6 +181,8 @@ one. It does not attempt to mix data from the two.
|
|||||||
|
|
||||||
|system()| does not support writing/reading "backgrounded" commands. |E5677|
|
|system()| does not support writing/reading "backgrounded" commands. |E5677|
|
||||||
|
|
||||||
|
|:redir| nested in |execute()| works.
|
||||||
|
|
||||||
Nvim may throttle (skip) messages from shell commands (|:!|, |:grep|, |:make|)
|
Nvim may throttle (skip) messages from shell commands (|:!|, |:grep|, |:make|)
|
||||||
if there is too much output. No data is lost, this only affects display and
|
if there is too much output. No data is lost, this only affects display and
|
||||||
makes things faster. |:terminal| output is never throttled.
|
makes things faster. |:terminal| output is never throttled.
|
||||||
@ -265,6 +274,8 @@ Lua interface (|if_lua.txt|):
|
|||||||
on cancel and completion respectively) via dictionary argument (replaces all
|
on cancel and completion respectively) via dictionary argument (replaces all
|
||||||
other arguments if used).
|
other arguments if used).
|
||||||
|
|
||||||
|
|input()| and |inputdialog()| now support user-defined cmdline highlighting.
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
5. Missing legacy features *nvim-features-missing*
|
5. Missing legacy features *nvim-features-missing*
|
||||||
|
|
||||||
|
@ -37,7 +37,72 @@ typedef struct {
|
|||||||
# include "api/private/ui_events_metadata.generated.h"
|
# include "api/private/ui_events_metadata.generated.h"
|
||||||
#endif
|
#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
|
/// 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)
|
void try_start(void)
|
||||||
{
|
{
|
||||||
++trylevel;
|
++trylevel;
|
||||||
@ -50,7 +115,9 @@ void try_start(void)
|
|||||||
/// @return true if an error occurred
|
/// @return true if an error occurred
|
||||||
bool try_end(Error *err)
|
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
|
// Without this it stops processing all subsequent VimL commands and
|
||||||
// generates strange error messages if I e.g. try calling Test() in a
|
// generates strange error messages if I e.g. try calling Test() in a
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include "nvim/api/private/defs.h"
|
#include "nvim/api/private/defs.h"
|
||||||
#include "nvim/vim.h"
|
#include "nvim/vim.h"
|
||||||
#include "nvim/memory.h"
|
#include "nvim/memory.h"
|
||||||
|
#include "nvim/ex_eval.h"
|
||||||
#include "nvim/lib/kvec.h"
|
#include "nvim/lib/kvec.h"
|
||||||
|
|
||||||
#define OBJECT_OBJ(o) o
|
#define OBJECT_OBJ(o) o
|
||||||
@ -82,6 +83,21 @@
|
|||||||
#define api_free_window(value)
|
#define api_free_window(value)
|
||||||
#define api_free_tabpage(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
|
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||||
# include "api/private/helpers.h.generated.h"
|
# include "api/private/helpers.h.generated.h"
|
||||||
#endif
|
#endif
|
||||||
|
@ -11031,6 +11031,7 @@ void get_user_input(const typval_T *const argvars,
|
|||||||
const char *defstr = "";
|
const char *defstr = "";
|
||||||
const char *cancelreturn = NULL;
|
const char *cancelreturn = NULL;
|
||||||
const char *xp_name = NULL;
|
const char *xp_name = NULL;
|
||||||
|
Callback input_callback = { .type = kCallbackNone };
|
||||||
char prompt_buf[NUMBUFLEN];
|
char prompt_buf[NUMBUFLEN];
|
||||||
char defstr_buf[NUMBUFLEN];
|
char defstr_buf[NUMBUFLEN];
|
||||||
char cancelreturn_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"));
|
emsgf(_("E5050: {opts} must be the only argument"));
|
||||||
return;
|
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, "");
|
prompt = tv_dict_get_string_buf_chk(dict, S_LEN("prompt"), prompt_buf, "");
|
||||||
if (prompt == NULL) {
|
if (prompt == NULL) {
|
||||||
return;
|
return;
|
||||||
@ -11066,6 +11067,9 @@ void get_user_input(const typval_T *const argvars,
|
|||||||
if (xp_name == def) { // default to NULL
|
if (xp_name == def) { // default to NULL
|
||||||
xp_name = NULL;
|
xp_name = NULL;
|
||||||
}
|
}
|
||||||
|
if (!tv_dict_get_callback(dict, S_LEN("highlight"), &input_callback)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
prompt = tv_get_string_buf_chk(&argvars[0], prompt_buf);
|
prompt = tv_get_string_buf_chk(&argvars[0], prompt_buf);
|
||||||
if (prompt == NULL) {
|
if (prompt == NULL) {
|
||||||
@ -11124,12 +11128,13 @@ void get_user_input(const typval_T *const argvars,
|
|||||||
|
|
||||||
stuffReadbuffSpec(defstr);
|
stuffReadbuffSpec(defstr);
|
||||||
|
|
||||||
int save_ex_normal_busy = ex_normal_busy;
|
const int save_ex_normal_busy = ex_normal_busy;
|
||||||
ex_normal_busy = 0;
|
ex_normal_busy = 0;
|
||||||
rettv->vval.v_string =
|
rettv->vval.v_string =
|
||||||
getcmdline_prompt(inputsecret_flag ? NUL : '@', (char_u *)p, echo_attr,
|
(char_u *)getcmdline_prompt(inputsecret_flag ? NUL : '@', p, echo_attr,
|
||||||
xp_type, (char_u *)xp_arg);
|
xp_type, xp_arg, input_callback);
|
||||||
ex_normal_busy = save_ex_normal_busy;
|
ex_normal_busy = save_ex_normal_busy;
|
||||||
|
callback_free(&input_callback);
|
||||||
|
|
||||||
if (rettv->vval.v_string == NULL && cancelreturn != NULL) {
|
if (rettv->vval.v_string == NULL && cancelreturn != NULL) {
|
||||||
rettv->vval.v_string = (char_u *)xstrdup(cancelreturn);
|
rettv->vval.v_string = (char_u *)xstrdup(cancelreturn);
|
||||||
|
@ -43,7 +43,7 @@ typedef struct partial_S partial_T;
|
|||||||
typedef struct ufunc ufunc_T;
|
typedef struct ufunc ufunc_T;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
kCallbackNone,
|
kCallbackNone = 0,
|
||||||
kCallbackFuncref,
|
kCallbackFuncref,
|
||||||
kCallbackPartial,
|
kCallbackPartial,
|
||||||
} CallbackType;
|
} CallbackType;
|
||||||
|
@ -189,7 +189,8 @@ void do_debug(char_u *cmd)
|
|||||||
}
|
}
|
||||||
|
|
||||||
xfree(cmdline);
|
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) {
|
if (typeahead_saved) {
|
||||||
restore_typeahead(&typeaheadbuf);
|
restore_typeahead(&typeaheadbuf);
|
||||||
|
@ -561,7 +561,9 @@ static void discard_exception(except_T *excp, int was_finished)
|
|||||||
*/
|
*/
|
||||||
void discard_current_exception(void)
|
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;
|
current_exception = NULL;
|
||||||
did_throw = FALSE;
|
did_throw = FALSE;
|
||||||
need_rethrow = FALSE;
|
need_rethrow = FALSE;
|
||||||
|
@ -63,6 +63,9 @@
|
|||||||
#include "nvim/os/os.h"
|
#include "nvim/os/os.h"
|
||||||
#include "nvim/event/loop.h"
|
#include "nvim/event/loop.h"
|
||||||
#include "nvim/os/time.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.
|
* Variables shared between getcmdline(), redrawcmdline() and others.
|
||||||
@ -70,23 +73,27 @@
|
|||||||
* structure.
|
* structure.
|
||||||
*/
|
*/
|
||||||
struct cmdline_info {
|
struct cmdline_info {
|
||||||
char_u *cmdbuff; /* pointer to command line buffer */
|
char_u *cmdbuff; // pointer to command line buffer
|
||||||
int cmdbufflen; /* length of cmdbuff */
|
int cmdbufflen; // length of cmdbuff
|
||||||
int cmdlen; /* number of chars in command line */
|
int cmdlen; // number of chars in command line
|
||||||
int cmdpos; /* current cursor position */
|
int cmdpos; // current cursor position
|
||||||
int cmdspos; /* cursor column on screen */
|
int cmdspos; // cursor column on screen
|
||||||
int cmdfirstc; /* ':', '/', '?', '=', '>' or NUL */
|
int cmdfirstc; // ':', '/', '?', '=', '>' or NUL
|
||||||
int cmdindent; /* number of spaces before cmdline */
|
int cmdindent; // number of spaces before cmdline
|
||||||
char_u *cmdprompt; /* message in front of cmdline */
|
char_u *cmdprompt; // message in front of cmdline
|
||||||
int cmdattr; /* attributes for prompt */
|
int cmdattr; // attributes for prompt
|
||||||
int overstrike; /* Typing mode on the command line. Shared by
|
int overstrike; // Typing mode on the command line. Shared by
|
||||||
getcmdline() and put_on_cmdline(). */
|
// getcmdline() and put_on_cmdline().
|
||||||
expand_T *xpc; /* struct being used for expansion, xp_pattern
|
expand_T *xpc; // struct being used for expansion, xp_pattern
|
||||||
may point into cmdbuff */
|
// may point into cmdbuff
|
||||||
int xp_context; /* type of expansion */
|
int xp_context; // type of expansion
|
||||||
char_u *xp_arg; /* user-defined expansion arg */
|
char_u *xp_arg; // user-defined expansion arg
|
||||||
int input_fn; /* when TRUE Invoked for input() function */
|
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 {
|
typedef struct command_line_state {
|
||||||
VimState state;
|
VimState state;
|
||||||
@ -136,6 +143,38 @@ typedef struct command_line_state {
|
|||||||
struct cmdline_info save_ccline;
|
struct cmdline_info save_ccline;
|
||||||
} CommandLineState;
|
} 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
|
/* The current cmdline_info. It is initialized in getcmdline() and after that
|
||||||
* used by other functions. When invoking getcmdline() recursively it needs
|
* used by other functions. When invoking getcmdline() recursively it needs
|
||||||
* to be saved with save_cmdline() and restored with restore_cmdline().
|
* 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 */
|
/* identifying (unique) number of newest history entry */
|
||||||
static int hislen = 0; /* actual length of history tables */
|
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
|
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||||
# include "ex_getln.c.generated.h"
|
# 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;
|
cmd_hkmap = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ccline.prompt_id = last_prompt_id++;
|
||||||
ccline.overstrike = false; // always start in insert mode
|
ccline.overstrike = false; // always start in insert mode
|
||||||
clearpos(&s->match_end);
|
clearpos(&s->match_end);
|
||||||
s->save_cursor = curwin->w_cursor; // may be restored later
|
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 ESC: // get here if p_wc != ESC or when ESC typed twice
|
||||||
case Ctrl_C:
|
case Ctrl_C:
|
||||||
// In exmode it doesn't make sense to return. Except when
|
// In exmode it doesn't make sense to return. Except when
|
||||||
// ":normal" runs out of characters.
|
// ":normal" runs out of characters. Also when highlight callback is active
|
||||||
if (exmode_active && (ex_normal_busy == 0 || typebuf.tb_len > 0)) {
|
// <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);
|
return command_line_not_changed(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1790,41 +1839,50 @@ getcmdline (
|
|||||||
return command_line_enter(firstc, count, indent);
|
return command_line_enter(firstc, count, indent);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/// Get a command line with a prompt
|
||||||
* Get a command line with a prompt.
|
///
|
||||||
* This is prepared to be called recursively from getcmdline() (e.g. by
|
/// This is prepared to be called recursively from getcmdline() (e.g. by
|
||||||
* f_input() when evaluating an expression from CTRL-R =).
|
/// f_input() when evaluating an expression from `<C-r>=`).
|
||||||
* Returns the command line in allocated memory, or NULL.
|
///
|
||||||
*/
|
/// @param[in] firstc Prompt type: e.g. '@' for input(), '>' for debug.
|
||||||
char_u *
|
/// @param[in] prompt Prompt string: what is displayed before the user text.
|
||||||
getcmdline_prompt (
|
/// @param[in] attr Prompt highlighting.
|
||||||
int firstc,
|
/// @param[in] xp_context Type of expansion.
|
||||||
char_u *prompt, /* command line prompt */
|
/// @param[in] xp_arg User-defined expansion argument.
|
||||||
int attr, /* attributes for prompt */
|
/// @param[in] highlight_callback Callback used for highlighting user input.
|
||||||
int xp_context, /* type of expansion */
|
///
|
||||||
char_u *xp_arg /* user-defined expansion argument */
|
/// @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;
|
const int msg_col_save = msg_col;
|
||||||
struct cmdline_info save_ccline;
|
|
||||||
int msg_col_save = msg_col;
|
|
||||||
|
|
||||||
|
struct cmdline_info save_ccline;
|
||||||
save_cmdline(&save_ccline);
|
save_cmdline(&save_ccline);
|
||||||
ccline.cmdprompt = prompt;
|
|
||||||
|
ccline.prompt_id = last_prompt_id++;
|
||||||
|
ccline.cmdprompt = (char_u *)prompt;
|
||||||
ccline.cmdattr = attr;
|
ccline.cmdattr = attr;
|
||||||
ccline.xp_context = xp_context;
|
ccline.xp_context = xp_context;
|
||||||
ccline.xp_arg = xp_arg;
|
ccline.xp_arg = (char_u *)xp_arg;
|
||||||
ccline.input_fn = (firstc == '@');
|
ccline.input_fn = (firstc == '@');
|
||||||
s = getcmdline(firstc, 1L, 0);
|
ccline.highlight_callback = highlight_callback;
|
||||||
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)
|
|
||||||
msg_col = msg_col_save;
|
|
||||||
|
|
||||||
return s;
|
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) {
|
||||||
|
msg_col = msg_col_save;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -2285,75 +2343,329 @@ void free_cmdline_buf(void)
|
|||||||
|
|
||||||
# endif
|
# 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
|
* Draw part of the cmdline at the current cursor position. But draw stars
|
||||||
* when cmdline_star is TRUE.
|
* when cmdline_star is TRUE.
|
||||||
*/
|
*/
|
||||||
static void draw_cmdline(int start, int len)
|
static void draw_cmdline(int start, int len)
|
||||||
{
|
{
|
||||||
int i;
|
if (!color_cmdline(&ccline, &last_ccline_colors)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (cmdline_star > 0)
|
if (cmdline_star > 0) {
|
||||||
for (i = 0; i < len; ++i) {
|
for (int i = 0; i < len; i++) {
|
||||||
msg_putchar('*');
|
msg_putchar('*');
|
||||||
if (has_mbyte)
|
if (has_mbyte) {
|
||||||
i += (*mb_ptr2len)(ccline.cmdbuff + start + i) - 1;
|
i += (*mb_ptr2len)(ccline.cmdbuff + start + i) - 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (p_arshape && !p_tbidi && enc_utf8 && len > 0) {
|
} else if (p_arshape && !p_tbidi && enc_utf8 && len > 0) {
|
||||||
static int buflen = 0;
|
bool do_arabicshape = false;
|
||||||
char_u *p;
|
|
||||||
int j;
|
|
||||||
int newlen = 0;
|
|
||||||
int mb_l;
|
int mb_l;
|
||||||
int pc, pc1 = 0;
|
for (int i = start; i < start + len; i += mb_l) {
|
||||||
int prev_c = 0;
|
char_u *p = ccline.cmdbuff + i;
|
||||||
int prev_c1 = 0;
|
int u8cc[MAX_MCO];
|
||||||
int u8c;
|
int u8c = utfc_ptr2char_len(p, u8cc, start + len - i);
|
||||||
int u8cc[MAX_MCO];
|
mb_l = utfc_ptr2len_len(p, start + len - i);
|
||||||
int nc = 0;
|
if (arabic_char(u8c)) {
|
||||||
|
do_arabicshape = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!do_arabicshape) {
|
||||||
|
goto draw_cmdline_no_arabicshape;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
static int buflen = 0;
|
||||||
* Do arabic shaping into a temporary buffer. This is very
|
|
||||||
* inefficient!
|
// Do arabic shaping into a temporary buffer. This is very
|
||||||
*/
|
// inefficient!
|
||||||
if (len * 2 + 2 > buflen) {
|
if (len * 2 + 2 > buflen) {
|
||||||
/* Re-allocate the buffer. We keep it around to avoid a lot of
|
// Re-allocate the buffer. We keep it around to avoid a lot of
|
||||||
* alloc()/free() calls. */
|
// alloc()/free() calls.
|
||||||
xfree(arshape_buf);
|
xfree(arshape_buf);
|
||||||
buflen = len * 2 + 2;
|
buflen = len * 2 + 2;
|
||||||
arshape_buf = xmalloc(buflen);
|
arshape_buf = xmalloc(buflen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int newlen = 0;
|
||||||
if (utf_iscomposing(utf_ptr2char(ccline.cmdbuff + start))) {
|
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] = ' ';
|
arshape_buf[0] = ' ';
|
||||||
newlen = 1;
|
newlen = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (j = start; j < start + len; j += mb_l) {
|
int prev_c = 0;
|
||||||
p = ccline.cmdbuff + j;
|
int prev_c1 = 0;
|
||||||
u8c = utfc_ptr2char_len(p, u8cc, start + len - j);
|
for (int i = start; i < start + len; i += mb_l) {
|
||||||
mb_l = utfc_ptr2len_len(p, start + len - j);
|
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)) {
|
if (arabic_char(u8c)) {
|
||||||
/* Do Arabic shaping. */
|
int pc;
|
||||||
|
int pc1 = 0;
|
||||||
|
int nc = 0;
|
||||||
|
// Do Arabic shaping.
|
||||||
if (cmdmsg_rl) {
|
if (cmdmsg_rl) {
|
||||||
/* displaying from right to left */
|
// Displaying from right to left.
|
||||||
pc = prev_c;
|
pc = prev_c;
|
||||||
pc1 = prev_c1;
|
pc1 = prev_c1;
|
||||||
prev_c1 = u8cc[0];
|
prev_c1 = u8cc[0];
|
||||||
if (j + mb_l >= start + len)
|
if (i + mb_l >= start + len) {
|
||||||
nc = NUL;
|
nc = NUL;
|
||||||
else
|
} else {
|
||||||
nc = utf_ptr2char(p + mb_l);
|
nc = utf_ptr2char(p + mb_l);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
/* displaying from left to right */
|
// Displaying from left to right.
|
||||||
if (j + mb_l >= start + len)
|
if (i + mb_l >= start + len) {
|
||||||
pc = NUL;
|
pc = NUL;
|
||||||
else {
|
} else {
|
||||||
int pcc[MAX_MCO];
|
int pcc[MAX_MCO];
|
||||||
|
|
||||||
pc = utfc_ptr2char_len(p + mb_l, pcc,
|
pc = utfc_ptr2char_len(p + mb_l, pcc, start + len - i - mb_l);
|
||||||
start + len - j - mb_l);
|
|
||||||
pc1 = pcc[0];
|
pc1 = pcc[0];
|
||||||
}
|
}
|
||||||
nc = prev_c;
|
nc = prev_c;
|
||||||
@ -2377,8 +2689,23 @@ static void draw_cmdline(int start, int len)
|
|||||||
}
|
}
|
||||||
|
|
||||||
msg_outtrans_len(arshape_buf, newlen);
|
msg_outtrans_len(arshape_buf, newlen);
|
||||||
} else
|
} else {
|
||||||
msg_outtrans_len(ccline.cmdbuff + start, len);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -75,7 +75,7 @@ struct interval {
|
|||||||
/*
|
/*
|
||||||
* Like utf8len_tab above, but using a zero for illegal lead bytes.
|
* 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,
|
||||||
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,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#ifndef NVIM_MBYTE_H
|
#ifndef NVIM_MBYTE_H
|
||||||
#define NVIM_MBYTE_H
|
#define NVIM_MBYTE_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
@ -67,6 +68,8 @@ typedef struct {
|
|||||||
///< otherwise use '?'.
|
///< otherwise use '?'.
|
||||||
} vimconv_T;
|
} vimconv_T;
|
||||||
|
|
||||||
|
extern const uint8_t utf8len_tab_zero[256];
|
||||||
|
|
||||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||||
# include "mbyte.h.generated.h"
|
# include "mbyte.h.generated.h"
|
||||||
#endif
|
#endif
|
||||||
|
@ -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().
|
* The display part of msg_puts_attr_len().
|
||||||
* May be called recursively to display scroll-back text.
|
* May be called recursively to display scroll-back text.
|
||||||
|
@ -207,7 +207,7 @@ nolog:
|
|||||||
# New style of tests uses Vim script with assert calls. These are easier
|
# New style of tests uses Vim script with assert calls. These are easier
|
||||||
# to write and a lot easier to read and debug.
|
# to write and a lot easier to read and debug.
|
||||||
# Limitation: Only works with the +eval feature.
|
# 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
|
newtests: newtestssilent
|
||||||
@/bin/sh -c "if test -f messages && grep -q 'FAILED' messages; then \
|
@/bin/sh -c "if test -f messages && grep -q 'FAILED' messages; then \
|
||||||
|
@ -23,10 +23,41 @@ before_each(function()
|
|||||||
function CustomListCompl(...)
|
function CustomListCompl(...)
|
||||||
return ['FOO']
|
return ['FOO']
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
highlight RBP1 guibg=Red
|
||||||
|
highlight RBP2 guibg=Yellow
|
||||||
|
highlight RBP3 guibg=Green
|
||||||
|
highlight RBP4 guibg=Blue
|
||||||
|
let g:NUM_LVLS = 4
|
||||||
|
function Redraw()
|
||||||
|
redraw!
|
||||||
|
return ''
|
||||||
|
endfunction
|
||||||
|
cnoremap <expr> {REDRAW} Redraw()
|
||||||
|
function RainBowParens(cmdline)
|
||||||
|
let ret = []
|
||||||
|
let i = 0
|
||||||
|
let lvl = 0
|
||||||
|
while i < len(a:cmdline)
|
||||||
|
if a:cmdline[i] is# '('
|
||||||
|
call add(ret, [i, i + 1, 'RBP' . ((lvl % g:NUM_LVLS) + 1)])
|
||||||
|
let lvl += 1
|
||||||
|
elseif a:cmdline[i] is# ')'
|
||||||
|
let lvl -= 1
|
||||||
|
call add(ret, [i, i + 1, 'RBP' . ((lvl % g:NUM_LVLS) + 1)])
|
||||||
|
endif
|
||||||
|
let i += 1
|
||||||
|
endwhile
|
||||||
|
return ret
|
||||||
|
endfunction
|
||||||
]])
|
]])
|
||||||
screen:set_default_attr_ids({
|
screen:set_default_attr_ids({
|
||||||
EOB={bold = true, foreground = Screen.colors.Blue1},
|
EOB={bold = true, foreground = Screen.colors.Blue1},
|
||||||
T={foreground=Screen.colors.Red},
|
T={foreground=Screen.colors.Red},
|
||||||
|
RBP1={background=Screen.colors.Red},
|
||||||
|
RBP2={background=Screen.colors.Yellow},
|
||||||
|
RBP3={background=Screen.colors.Green},
|
||||||
|
RBP4={background=Screen.colors.Blue},
|
||||||
})
|
})
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@ -196,6 +227,18 @@ describe('input()', function()
|
|||||||
eq('Vim(call):E118: Too many arguments for function: input',
|
eq('Vim(call):E118: Too many arguments for function: input',
|
||||||
exc_exec('call input("prompt> ", "default", "file", "extra")'))
|
exc_exec('call input("prompt> ", "default", "file", "extra")'))
|
||||||
end)
|
end)
|
||||||
|
it('supports highlighting', function()
|
||||||
|
command('nnoremap <expr> X input({"highlight": "RainBowParens"})[-1]')
|
||||||
|
feed([[X]])
|
||||||
|
feed('(())')
|
||||||
|
screen:expect([[
|
||||||
|
|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{RBP1:(}{RBP2:()}{RBP1:)}^ |
|
||||||
|
]])
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
describe('inputdialog()', function()
|
describe('inputdialog()', function()
|
||||||
it('works with multiline prompts', function()
|
it('works with multiline prompts', function()
|
||||||
@ -363,4 +406,16 @@ describe('inputdialog()', function()
|
|||||||
eq('Vim(call):E118: Too many arguments for function: inputdialog',
|
eq('Vim(call):E118: Too many arguments for function: inputdialog',
|
||||||
exc_exec('call inputdialog("prompt> ", "default", "file", "extra")'))
|
exc_exec('call inputdialog("prompt> ", "default", "file", "extra")'))
|
||||||
end)
|
end)
|
||||||
|
it('supports highlighting', function()
|
||||||
|
command('nnoremap <expr> X inputdialog({"highlight": "RainBowParens"})[-1]')
|
||||||
|
feed([[X]])
|
||||||
|
feed('(())')
|
||||||
|
screen:expect([[
|
||||||
|
|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{RBP1:(}{RBP2:()}{RBP1:)}^ |
|
||||||
|
]])
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
893
test/functional/ui/cmdline_highlight_spec.lua
Normal file
893
test/functional/ui/cmdline_highlight_spec.lua
Normal file
@ -0,0 +1,893 @@
|
|||||||
|
local helpers = require('test.functional.helpers')(after_each)
|
||||||
|
local Screen = require('test.functional.ui.screen')
|
||||||
|
|
||||||
|
local eq = helpers.eq
|
||||||
|
local feed = helpers.feed
|
||||||
|
local clear = helpers.clear
|
||||||
|
local meths = helpers.meths
|
||||||
|
local funcs = helpers.funcs
|
||||||
|
local source = helpers.source
|
||||||
|
local dedent = helpers.dedent
|
||||||
|
local command = helpers.command
|
||||||
|
local curbufmeths = helpers.curbufmeths
|
||||||
|
|
||||||
|
local screen
|
||||||
|
|
||||||
|
-- Bug in input() handling: :redraw! will erase the whole prompt up until
|
||||||
|
-- user types something. It exists in Vim as well, so using `h<BS>` as
|
||||||
|
-- a workaround.
|
||||||
|
local function redraw_input()
|
||||||
|
feed('{REDRAW}h<BS>')
|
||||||
|
end
|
||||||
|
|
||||||
|
before_each(function()
|
||||||
|
clear()
|
||||||
|
screen = Screen.new(40, 8)
|
||||||
|
screen:attach()
|
||||||
|
source([[
|
||||||
|
highlight RBP1 guibg=Red
|
||||||
|
highlight RBP2 guibg=Yellow
|
||||||
|
highlight RBP3 guibg=Green
|
||||||
|
highlight RBP4 guibg=Blue
|
||||||
|
let g:NUM_LVLS = 4
|
||||||
|
function Redraw()
|
||||||
|
redraw!
|
||||||
|
return ''
|
||||||
|
endfunction
|
||||||
|
let g:id = ''
|
||||||
|
cnoremap <expr> {REDRAW} Redraw()
|
||||||
|
function DoPrompt(do_return) abort
|
||||||
|
let id = g:id
|
||||||
|
let Cb = g:Nvim_color_input{g:id}
|
||||||
|
let out = input({'prompt': ':', 'highlight': Cb})
|
||||||
|
let g:out{id} = out
|
||||||
|
return (a:do_return ? out : '')
|
||||||
|
endfunction
|
||||||
|
nnoremap <expr> {PROMPT} DoPrompt(0)
|
||||||
|
cnoremap <expr> {PROMPT} DoPrompt(1)
|
||||||
|
function RainBowParens(cmdline)
|
||||||
|
let ret = []
|
||||||
|
let i = 0
|
||||||
|
let lvl = 0
|
||||||
|
while i < len(a:cmdline)
|
||||||
|
if a:cmdline[i] is# '('
|
||||||
|
call add(ret, [i, i + 1, 'RBP' . ((lvl % g:NUM_LVLS) + 1)])
|
||||||
|
let lvl += 1
|
||||||
|
elseif a:cmdline[i] is# ')'
|
||||||
|
let lvl -= 1
|
||||||
|
call add(ret, [i, i + 1, 'RBP' . ((lvl % g:NUM_LVLS) + 1)])
|
||||||
|
endif
|
||||||
|
let i += 1
|
||||||
|
endwhile
|
||||||
|
return ret
|
||||||
|
endfunction
|
||||||
|
function SplittedMultibyteStart(cmdline)
|
||||||
|
let ret = []
|
||||||
|
let i = 0
|
||||||
|
while i < len(a:cmdline)
|
||||||
|
let char = nr2char(char2nr(a:cmdline[i:]))
|
||||||
|
if a:cmdline[i:i + len(char) - 1] is# char
|
||||||
|
if len(char) > 1
|
||||||
|
call add(ret, [i + 1, i + len(char), 'RBP2'])
|
||||||
|
endif
|
||||||
|
let i += len(char)
|
||||||
|
else
|
||||||
|
let i += 1
|
||||||
|
endif
|
||||||
|
endwhile
|
||||||
|
return ret
|
||||||
|
endfunction
|
||||||
|
function SplittedMultibyteEnd(cmdline)
|
||||||
|
let ret = []
|
||||||
|
let i = 0
|
||||||
|
while i < len(a:cmdline)
|
||||||
|
let char = nr2char(char2nr(a:cmdline[i:]))
|
||||||
|
if a:cmdline[i:i + len(char) - 1] is# char
|
||||||
|
if len(char) > 1
|
||||||
|
call add(ret, [i, i + 1, 'RBP1'])
|
||||||
|
endif
|
||||||
|
let i += len(char)
|
||||||
|
else
|
||||||
|
let i += 1
|
||||||
|
endif
|
||||||
|
endwhile
|
||||||
|
return ret
|
||||||
|
endfunction
|
||||||
|
function Echoing(cmdline)
|
||||||
|
echo 'HERE'
|
||||||
|
return v:_null_list
|
||||||
|
endfunction
|
||||||
|
function Echoning(cmdline)
|
||||||
|
echon 'HERE'
|
||||||
|
return v:_null_list
|
||||||
|
endfunction
|
||||||
|
function Echomsging(cmdline)
|
||||||
|
echomsg 'HERE'
|
||||||
|
return v:_null_list
|
||||||
|
endfunction
|
||||||
|
function Echoerring(cmdline)
|
||||||
|
echoerr 'HERE'
|
||||||
|
return v:_null_list
|
||||||
|
endfunction
|
||||||
|
function Redrawing(cmdline)
|
||||||
|
redraw!
|
||||||
|
return v:_null_list
|
||||||
|
endfunction
|
||||||
|
function Throwing(cmdline)
|
||||||
|
throw "ABC"
|
||||||
|
return v:_null_list
|
||||||
|
endfunction
|
||||||
|
function Halting(cmdline)
|
||||||
|
while 1
|
||||||
|
endwhile
|
||||||
|
endfunction
|
||||||
|
function ReturningGlobal(cmdline)
|
||||||
|
return g:callback_return
|
||||||
|
endfunction
|
||||||
|
function ReturningGlobal2(cmdline)
|
||||||
|
return g:callback_return[:len(a:cmdline)-1]
|
||||||
|
endfunction
|
||||||
|
function ReturningGlobalN(n, cmdline)
|
||||||
|
return g:callback_return{a:n}
|
||||||
|
endfunction
|
||||||
|
let g:recording_calls = []
|
||||||
|
function Recording(cmdline)
|
||||||
|
call add(g:recording_calls, a:cmdline)
|
||||||
|
return []
|
||||||
|
endfunction
|
||||||
|
]])
|
||||||
|
screen:set_default_attr_ids({
|
||||||
|
RBP1={background = Screen.colors.Red},
|
||||||
|
RBP2={background = Screen.colors.Yellow},
|
||||||
|
RBP3={background = Screen.colors.Green},
|
||||||
|
RBP4={background = Screen.colors.Blue},
|
||||||
|
EOB={bold = true, foreground = Screen.colors.Blue1},
|
||||||
|
ERR={foreground = Screen.colors.Grey100, background = Screen.colors.Red},
|
||||||
|
SK={foreground = Screen.colors.Blue},
|
||||||
|
PE={bold = true, foreground = Screen.colors.SeaGreen4}
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
|
||||||
|
local function set_color_cb(funcname, callback_return, id)
|
||||||
|
meths.set_var('id', id or '')
|
||||||
|
if id and id ~= '' and funcs.exists('*' .. funcname .. 'N') then
|
||||||
|
command(('let g:Nvim_color_input%s = {cmdline -> %sN(%s, cmdline)}'):format(
|
||||||
|
id, funcname, id))
|
||||||
|
if callback_return then
|
||||||
|
meths.set_var('callback_return' .. id, callback_return)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
meths.set_var('Nvim_color_input', funcname)
|
||||||
|
if callback_return then
|
||||||
|
meths.set_var('callback_return', callback_return)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local function start_prompt(text)
|
||||||
|
feed('{PROMPT}' .. (text or ''))
|
||||||
|
end
|
||||||
|
|
||||||
|
describe('Command-line coloring', function()
|
||||||
|
it('works', function()
|
||||||
|
set_color_cb('RainBowParens')
|
||||||
|
meths.set_option('more', false)
|
||||||
|
start_prompt()
|
||||||
|
screen:expect([[
|
||||||
|
|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
:^ |
|
||||||
|
]])
|
||||||
|
feed('e')
|
||||||
|
screen:expect([[
|
||||||
|
|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
:e^ |
|
||||||
|
]])
|
||||||
|
feed('cho ')
|
||||||
|
screen:expect([[
|
||||||
|
|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
:echo ^ |
|
||||||
|
]])
|
||||||
|
feed('(')
|
||||||
|
screen:expect([[
|
||||||
|
|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
:echo {RBP1:(}^ |
|
||||||
|
]])
|
||||||
|
feed('(')
|
||||||
|
screen:expect([[
|
||||||
|
|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
:echo {RBP1:(}{RBP2:(}^ |
|
||||||
|
]])
|
||||||
|
feed('42')
|
||||||
|
screen:expect([[
|
||||||
|
|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
:echo {RBP1:(}{RBP2:(}42^ |
|
||||||
|
]])
|
||||||
|
feed('))')
|
||||||
|
screen:expect([[
|
||||||
|
|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
:echo {RBP1:(}{RBP2:(}42{RBP2:)}{RBP1:)}^ |
|
||||||
|
]])
|
||||||
|
feed('<BS>')
|
||||||
|
screen:expect([[
|
||||||
|
|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
:echo {RBP1:(}{RBP2:(}42{RBP2:)}^ |
|
||||||
|
]])
|
||||||
|
redraw_input()
|
||||||
|
screen:expect([[
|
||||||
|
|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
:echo {RBP1:(}{RBP2:(}42{RBP2:)}^ |
|
||||||
|
]])
|
||||||
|
end)
|
||||||
|
for _, func_part in ipairs({'', 'n', 'msg'}) do
|
||||||
|
it('disables :echo' .. func_part .. ' messages', function()
|
||||||
|
set_color_cb('Echo' .. func_part .. 'ing')
|
||||||
|
start_prompt('echo')
|
||||||
|
screen:expect([[
|
||||||
|
|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
:echo^ |
|
||||||
|
]])
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
it('does the right thing when hl start appears to split multibyte char',
|
||||||
|
function()
|
||||||
|
set_color_cb('SplittedMultibyteStart')
|
||||||
|
start_prompt('echo "«')
|
||||||
|
screen:expect([[
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
:echo " |
|
||||||
|
{ERR:E5405: Chunk 0 start 7 splits multibyte }|
|
||||||
|
{ERR:character} |
|
||||||
|
:echo "«^ |
|
||||||
|
]])
|
||||||
|
feed('»')
|
||||||
|
screen:expect([[
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
:echo " |
|
||||||
|
{ERR:E5405: Chunk 0 start 7 splits multibyte }|
|
||||||
|
{ERR:character} |
|
||||||
|
:echo "«»^ |
|
||||||
|
]])
|
||||||
|
end)
|
||||||
|
it('does the right thing when hl end appears to split multibyte char',
|
||||||
|
function()
|
||||||
|
set_color_cb('SplittedMultibyteEnd')
|
||||||
|
start_prompt('echo "«')
|
||||||
|
screen:expect([[
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
:echo " |
|
||||||
|
{ERR:E5406: Chunk 0 end 7 splits multibyte ch}|
|
||||||
|
{ERR:aracter} |
|
||||||
|
:echo "«^ |
|
||||||
|
]])
|
||||||
|
end)
|
||||||
|
it('does the right thing when errorring', function()
|
||||||
|
set_color_cb('Echoerring')
|
||||||
|
start_prompt('e')
|
||||||
|
screen:expect([[
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
: |
|
||||||
|
{ERR:E5407: Callback has thrown an exception:}|
|
||||||
|
{ERR: Vim(echoerr):HERE} |
|
||||||
|
:e^ |
|
||||||
|
]])
|
||||||
|
end)
|
||||||
|
it('silences :echo', function()
|
||||||
|
set_color_cb('Echoing')
|
||||||
|
start_prompt('e')
|
||||||
|
screen:expect([[
|
||||||
|
|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
:e^ |
|
||||||
|
]])
|
||||||
|
eq('', meths.command_output('messages'))
|
||||||
|
end)
|
||||||
|
it('silences :echon', function()
|
||||||
|
set_color_cb('Echoning')
|
||||||
|
start_prompt('e')
|
||||||
|
screen:expect([[
|
||||||
|
|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
:e^ |
|
||||||
|
]])
|
||||||
|
eq('', meths.command_output('messages'))
|
||||||
|
end)
|
||||||
|
it('silences :echomsg', function()
|
||||||
|
set_color_cb('Echomsging')
|
||||||
|
start_prompt('e')
|
||||||
|
screen:expect([[
|
||||||
|
|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
:e^ |
|
||||||
|
]])
|
||||||
|
eq('', meths.command_output('messages'))
|
||||||
|
end)
|
||||||
|
it('does the right thing when throwing', function()
|
||||||
|
set_color_cb('Throwing')
|
||||||
|
start_prompt('e')
|
||||||
|
screen:expect([[
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
: |
|
||||||
|
{ERR:E5407: Callback has thrown an exception:}|
|
||||||
|
{ERR: ABC} |
|
||||||
|
:e^ |
|
||||||
|
]])
|
||||||
|
end)
|
||||||
|
it('stops executing callback after a number of errors', function()
|
||||||
|
set_color_cb('SplittedMultibyteStart')
|
||||||
|
start_prompt('let x = "«»«»«»«»«»"\n')
|
||||||
|
screen:expect([[
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
:let x = " |
|
||||||
|
{ERR:E5405: Chunk 0 start 10 splits multibyte}|
|
||||||
|
{ERR: character} |
|
||||||
|
^:let x = "«»«»«»«»«»" |
|
||||||
|
]])
|
||||||
|
feed('\n')
|
||||||
|
screen:expect([[
|
||||||
|
^ |
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
|
|
||||||
|
]])
|
||||||
|
eq('let x = "«»«»«»«»«»"', meths.get_var('out'))
|
||||||
|
local msg = '\nE5405: Chunk 0 start 10 splits multibyte character'
|
||||||
|
eq(msg:rep(1), funcs.execute('messages'))
|
||||||
|
end)
|
||||||
|
it('allows interrupting callback with <C-c>', function()
|
||||||
|
set_color_cb('Halting')
|
||||||
|
start_prompt('echo 42')
|
||||||
|
screen:expect([[
|
||||||
|
^ |
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
|
|
||||||
|
]])
|
||||||
|
screen:sleep(500)
|
||||||
|
feed('<C-c>')
|
||||||
|
screen:expect([[
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
: |
|
||||||
|
{ERR:E5407: Callback has thrown an exception:}|
|
||||||
|
{ERR: Keyboard interrupt} |
|
||||||
|
:echo 42^ |
|
||||||
|
]])
|
||||||
|
redraw_input()
|
||||||
|
screen:expect([[
|
||||||
|
|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
:echo 42^ |
|
||||||
|
]])
|
||||||
|
feed('\n')
|
||||||
|
screen:expect([[
|
||||||
|
|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
^:echo 42 |
|
||||||
|
]])
|
||||||
|
feed('\n')
|
||||||
|
eq('echo 42', meths.get_var('out'))
|
||||||
|
feed('<C-c>')
|
||||||
|
screen:expect([[
|
||||||
|
^ |
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
Type :quit<Enter> to exit Nvim |
|
||||||
|
]])
|
||||||
|
end)
|
||||||
|
it('works fine with NUL, NL, CR', function()
|
||||||
|
set_color_cb('RainBowParens')
|
||||||
|
start_prompt('echo ("<C-v><CR><C-v><Nul><C-v><NL>")')
|
||||||
|
screen:expect([[
|
||||||
|
|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
:echo {RBP1:(}"{SK:^M^@^@}"{RBP1:)}^ |
|
||||||
|
]])
|
||||||
|
end)
|
||||||
|
it('errors out when callback returns something wrong', function()
|
||||||
|
command('cnoremap + ++')
|
||||||
|
set_color_cb('ReturningGlobal', '')
|
||||||
|
start_prompt('#')
|
||||||
|
screen:expect([[
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
: |
|
||||||
|
{ERR:E5400: Callback should return list} |
|
||||||
|
:#^ |
|
||||||
|
]])
|
||||||
|
|
||||||
|
feed('<CR><CR><CR>')
|
||||||
|
set_color_cb('ReturningGlobal', {{0, 1, 'Normal'}, 42})
|
||||||
|
start_prompt('#')
|
||||||
|
screen:expect([[
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
: |
|
||||||
|
{ERR:E5401: List item 1 is not a List} |
|
||||||
|
:#^ |
|
||||||
|
]])
|
||||||
|
|
||||||
|
feed('<CR><CR><CR>')
|
||||||
|
set_color_cb('ReturningGlobal2', {{0, 1, 'Normal'}, {1}})
|
||||||
|
start_prompt('+')
|
||||||
|
screen:expect([[
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
:+ |
|
||||||
|
{ERR:E5402: List item 1 has incorrect length:}|
|
||||||
|
{ERR: 1 /= 3} |
|
||||||
|
:++^ |
|
||||||
|
]])
|
||||||
|
|
||||||
|
feed('<CR><CR><CR>')
|
||||||
|
set_color_cb('ReturningGlobal2', {{0, 1, 'Normal'}, {2, 3, 'Normal'}})
|
||||||
|
start_prompt('+')
|
||||||
|
screen:expect([[
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
:+ |
|
||||||
|
{ERR:E5403: Chunk 1 start 2 not in range [1, }|
|
||||||
|
{ERR:2)} |
|
||||||
|
:++^ |
|
||||||
|
]])
|
||||||
|
|
||||||
|
feed('<CR><CR><CR>')
|
||||||
|
set_color_cb('ReturningGlobal2', {{0, 1, 'Normal'}, {1, 3, 'Normal'}})
|
||||||
|
start_prompt('+')
|
||||||
|
screen:expect([[
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
:+ |
|
||||||
|
{ERR:E5404: Chunk 1 end 3 not in range (1, 2]}|
|
||||||
|
|
|
||||||
|
:++^ |
|
||||||
|
]])
|
||||||
|
end)
|
||||||
|
it('does not error out when called from a errorred out cycle', function()
|
||||||
|
set_color_cb('ReturningGlobal', {{0, 1, 'Normal'}})
|
||||||
|
feed(dedent([[
|
||||||
|
:set regexpengine=2
|
||||||
|
:for pat in [' \ze*', ' \zs*']
|
||||||
|
: try
|
||||||
|
: let l = matchlist('x x', pat)
|
||||||
|
: $put =input({'prompt':'>','highlight':'ReturningGlobal'})
|
||||||
|
:
|
||||||
|
: $put ='E888 NOT detected for ' . pat
|
||||||
|
: catch
|
||||||
|
: $put =input({'prompt':'>','highlight':'ReturningGlobal'})
|
||||||
|
:
|
||||||
|
: $put ='E888 detected for ' . pat
|
||||||
|
: endtry
|
||||||
|
:endfor
|
||||||
|
:
|
||||||
|
:
|
||||||
|
:
|
||||||
|
:
|
||||||
|
:
|
||||||
|
:
|
||||||
|
]]))
|
||||||
|
eq({'', ':', 'E888 detected for \\ze*', ':', 'E888 detected for \\zs*'},
|
||||||
|
curbufmeths.get_lines(0, -1, false))
|
||||||
|
eq('', funcs.execute('messages'))
|
||||||
|
end)
|
||||||
|
it('allows nesting input()s', function()
|
||||||
|
set_color_cb('ReturningGlobal', {{0, 1, 'RBP1'}}, '')
|
||||||
|
start_prompt('1')
|
||||||
|
screen:expect([[
|
||||||
|
|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
:{RBP1:1}^ |
|
||||||
|
]])
|
||||||
|
|
||||||
|
set_color_cb('ReturningGlobal', {{0, 1, 'RBP2'}}, '1')
|
||||||
|
start_prompt('2')
|
||||||
|
screen:expect([[
|
||||||
|
|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
:{RBP2:2}^ |
|
||||||
|
]])
|
||||||
|
|
||||||
|
set_color_cb('ReturningGlobal', {{0, 1, 'RBP3'}}, '2')
|
||||||
|
start_prompt('3')
|
||||||
|
screen:expect([[
|
||||||
|
|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
:{RBP3:3}^ |
|
||||||
|
]])
|
||||||
|
|
||||||
|
set_color_cb('ReturningGlobal', {{0, 1, 'RBP4'}}, '3')
|
||||||
|
start_prompt('4')
|
||||||
|
screen:expect([[
|
||||||
|
|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
:{RBP4:4}^ |
|
||||||
|
]])
|
||||||
|
|
||||||
|
feed('<CR>')
|
||||||
|
screen:expect([[
|
||||||
|
|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
:{RBP3:3}4^ |
|
||||||
|
]])
|
||||||
|
feed('<CR>')
|
||||||
|
screen:expect([[
|
||||||
|
|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
:{RBP2:2}34^ |
|
||||||
|
]])
|
||||||
|
feed('<CR>')
|
||||||
|
screen:expect([[
|
||||||
|
|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
:{RBP1:1}234^ |
|
||||||
|
]])
|
||||||
|
feed('<CR><CR><C-l>')
|
||||||
|
screen:expect([[
|
||||||
|
^ |
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
|
|
||||||
|
]])
|
||||||
|
eq('1234', meths.get_var('out'))
|
||||||
|
eq('234', meths.get_var('out1'))
|
||||||
|
eq('34', meths.get_var('out2'))
|
||||||
|
eq('4', meths.get_var('out3'))
|
||||||
|
eq(0, funcs.exists('g:out4'))
|
||||||
|
end)
|
||||||
|
it('runs callback with the same data only once', function()
|
||||||
|
local function new_recording_calls(...)
|
||||||
|
eq({...}, meths.get_var('recording_calls'))
|
||||||
|
meths.set_var('recording_calls', {})
|
||||||
|
end
|
||||||
|
set_color_cb('Recording')
|
||||||
|
start_prompt('')
|
||||||
|
-- Regression test. Disambiguation:
|
||||||
|
--
|
||||||
|
-- new_recording_calls(expected_result) -- (actual_before_fix)
|
||||||
|
--
|
||||||
|
feed('a')
|
||||||
|
new_recording_calls('a') -- ('a', 'a')
|
||||||
|
feed('b')
|
||||||
|
new_recording_calls('ab') -- ('a', 'ab', 'ab')
|
||||||
|
feed('c')
|
||||||
|
new_recording_calls('abc') -- ('ab', 'abc', 'abc')
|
||||||
|
feed('<BS>')
|
||||||
|
new_recording_calls('ab') -- ('abc', 'ab', 'ab')
|
||||||
|
feed('<BS>')
|
||||||
|
new_recording_calls('a') -- ('ab', 'a', 'a')
|
||||||
|
feed('<BS>')
|
||||||
|
new_recording_calls() -- ('a')
|
||||||
|
feed('<CR><CR>')
|
||||||
|
eq('', meths.get_var('out'))
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
describe('Ex commands coloring support', function()
|
||||||
|
it('works', function()
|
||||||
|
meths.set_var('Nvim_color_cmdline', 'RainBowParens')
|
||||||
|
feed(':echo (((1)))')
|
||||||
|
screen:expect([[
|
||||||
|
|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
:echo {RBP1:(}{RBP2:(}{RBP3:(}1{RBP3:)}{RBP2:)}{RBP1:)}^ |
|
||||||
|
]])
|
||||||
|
end)
|
||||||
|
it('still executes command-line even if errored out', function()
|
||||||
|
meths.set_var('Nvim_color_cmdline', 'SplittedMultibyteStart')
|
||||||
|
feed(':let x = "«"\n')
|
||||||
|
eq('«', meths.get_var('x'))
|
||||||
|
local msg = 'E5405: Chunk 0 start 10 splits multibyte character'
|
||||||
|
eq('\n'..msg, funcs.execute('messages'))
|
||||||
|
end)
|
||||||
|
it('does not error out when called from a errorred out cycle', function()
|
||||||
|
-- Apparently when there is a cycle in which one of the commands errors out
|
||||||
|
-- this error may be caught by color_cmdline before it is presented to the
|
||||||
|
-- user.
|
||||||
|
feed(dedent([[
|
||||||
|
:set regexpengine=2
|
||||||
|
:for pat in [' \ze*', ' \zs*']
|
||||||
|
: try
|
||||||
|
: let l = matchlist('x x', pat)
|
||||||
|
: $put ='E888 NOT detected for ' . pat
|
||||||
|
: catch
|
||||||
|
: $put ='E888 detected for ' . pat
|
||||||
|
: endtry
|
||||||
|
:endfor
|
||||||
|
]]))
|
||||||
|
eq({'', 'E888 detected for \\ze*', 'E888 detected for \\zs*'},
|
||||||
|
curbufmeths.get_lines(0, -1, false))
|
||||||
|
eq('', funcs.execute('messages'))
|
||||||
|
end)
|
||||||
|
it('does not crash when using `n` in debug mode', function()
|
||||||
|
feed(':debug execute "echo 1"\n')
|
||||||
|
screen:expect([[
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
Entering Debug mode. Type "cont" to con|
|
||||||
|
tinue. |
|
||||||
|
cmd: execute "echo 1" |
|
||||||
|
>^ |
|
||||||
|
]])
|
||||||
|
feed('n\n')
|
||||||
|
screen:expect([[
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
Entering Debug mode. Type "cont" to con|
|
||||||
|
tinue. |
|
||||||
|
cmd: execute "echo 1" |
|
||||||
|
>n |
|
||||||
|
1 |
|
||||||
|
{PE:Press ENTER or type command to continue}^ |
|
||||||
|
]])
|
||||||
|
feed('\n')
|
||||||
|
screen:expect([[
|
||||||
|
^ |
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
|
|
||||||
|
]])
|
||||||
|
end)
|
||||||
|
it('does not prevent mapping error from cancelling prompt', function()
|
||||||
|
command("cnoremap <expr> x execute('throw 42')[-1]")
|
||||||
|
feed(':#x')
|
||||||
|
screen:expect([[
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
:# |
|
||||||
|
{ERR:Error detected while processing :} |
|
||||||
|
{ERR:E605: Exception not caught: 42} |
|
||||||
|
:#^ |
|
||||||
|
]])
|
||||||
|
feed('<CR>')
|
||||||
|
screen:expect([[
|
||||||
|
^ |
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
|
|
||||||
|
]])
|
||||||
|
feed('<CR>')
|
||||||
|
screen:expect([[
|
||||||
|
^ |
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
|
|
||||||
|
]])
|
||||||
|
eq('\nError detected while processing :\nE605: Exception not caught: 42',
|
||||||
|
meths.command_output('messages'))
|
||||||
|
end)
|
||||||
|
it('errors out when failing to get callback', function()
|
||||||
|
meths.set_var('Nvim_color_cmdline', 42)
|
||||||
|
feed(':#')
|
||||||
|
screen:expect([[
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
: |
|
||||||
|
{ERR:E5408: Unable to get g:Nvim_color_cmdlin}|
|
||||||
|
{ERR:e callback: Vim:E6000: Argument is not a}|
|
||||||
|
{ERR: function or function name} |
|
||||||
|
:#^ |
|
||||||
|
]])
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
describe('Expressions coloring support', function()
|
||||||
|
it('works', function()
|
||||||
|
meths.set_var('Nvim_color_expr', 'RainBowParens')
|
||||||
|
feed(':echo <C-r>=(((1)))')
|
||||||
|
screen:expect([[
|
||||||
|
|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
={RBP1:(}{RBP2:(}{RBP3:(}1{RBP3:)}{RBP2:)}{RBP1:)}^ |
|
||||||
|
]])
|
||||||
|
end)
|
||||||
|
it('errors out when failing to get callback', function()
|
||||||
|
meths.set_var('Nvim_color_expr', 42)
|
||||||
|
feed(':<C-r>=1')
|
||||||
|
screen:expect([[
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
{EOB:~ }|
|
||||||
|
= |
|
||||||
|
{ERR:E5409: Unable to get g:Nvim_color_expr c}|
|
||||||
|
{ERR:allback: Vim:E6000: Argument is not a fu}|
|
||||||
|
{ERR:nction or function name} |
|
||||||
|
=1^ |
|
||||||
|
]])
|
||||||
|
end)
|
||||||
|
end)
|
@ -251,7 +251,7 @@ function Screen:expect(expected, attr_ids, attr_ignore, condition, any)
|
|||||||
..'Expected:\n |'..table.concat(msg_expected_rows, '|\n |')..'|\n'
|
..'Expected:\n |'..table.concat(msg_expected_rows, '|\n |')..'|\n'
|
||||||
..'Actual:\n |'..table.concat(actual_rows, '|\n |')..'|\n\n'..[[
|
..'Actual:\n |'..table.concat(actual_rows, '|\n |')..'|\n\n'..[[
|
||||||
To print the expect() call that would assert the current screen state, use
|
To print the expect() call that would assert the current screen state, use
|
||||||
screen:snaphot_util(). In case of non-deterministic failures, use
|
screen:snapshot_util(). In case of non-deterministic failures, use
|
||||||
screen:redraw_debug() to show all intermediate screen states. ]])
|
screen:redraw_debug() to show all intermediate screen states. ]])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user