aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--man/nvim.121
-rw-r--r--runtime/doc/msgpack_rpc.txt24
-rw-r--r--runtime/doc/options.txt26
-rw-r--r--runtime/doc/vim_diff.txt1
-rw-r--r--src/nvim/api/ui.c10
-rw-r--r--src/nvim/cursor_shape.c137
-rw-r--r--src/nvim/cursor_shape.h73
-rw-r--r--src/nvim/options.lua2
-rw-r--r--src/nvim/syntax.c94
-rw-r--r--src/nvim/syntax.h10
-rw-r--r--src/nvim/tui/tui.c186
-rw-r--r--src/nvim/tui/tui.h2
-rw-r--r--src/nvim/ui.c9
-rw-r--r--src/nvim/ui.h1
-rw-r--r--src/nvim/ui_bridge.c19
-rw-r--r--test/functional/ui/cursor_spec.lua182
-rw-r--r--test/functional/ui/mouse_spec.lua2
-rw-r--r--test/functional/ui/screen.lua6
18 files changed, 583 insertions, 222 deletions
diff --git a/man/nvim.1 b/man/nvim.1
index 98d97c2d5a..d2a3ea5c43 100644
--- a/man/nvim.1
+++ b/man/nvim.1
@@ -371,27 +371,6 @@ See
Used to set the 'shell' option, which determines the shell used by the
.Ic :terminal
command.
-.It Ev NVIM_TUI_ENABLE_CURSOR_SHAPE
-Set to 0 to prevent Nvim from changing the cursor shape.
-Set to 1 to enable non-blinking mode-sensitive cursor (this is the default).
-Set to 2 to enable blinking mode-sensitive cursor.
-Host terminal must support the DECSCUSR CSI escape sequence.
-.Pp
-Depending on the terminal emulator, using this option with
-.Nm
-under
-.Xr tmux 1
-might require adding the following to
-.Pa ~/.tmux.conf :
-.Bd -literal -offset indent
-set -ga terminal-overrides ',*:Ss=\eE[%p1%d q:Se=\eE[2 q'
-.Ed
-.Pp
-See
-.Ic terminal-overrides
-in the
-.Xr tmux 1
-manual page for more information.
.El
.Sh FILES
.Bl -tag -width "~/.config/nvim/init.vim"
diff --git a/runtime/doc/msgpack_rpc.txt b/runtime/doc/msgpack_rpc.txt
index 02086e24fd..3f3c41f566 100644
--- a/runtime/doc/msgpack_rpc.txt
+++ b/runtime/doc/msgpack_rpc.txt
@@ -250,23 +250,21 @@ connect to another with different type codes.
==============================================================================
6. Remote UIs *rpc-remote-ui*
-Nvim allows Graphical user interfaces to be implemented by separate processes
-communicating with Nvim over the RPC API. Currently the ui model conists of a
-terminal-like grid with one single, monospace font size, with a few elements
-that could be drawn separately from the grid (for the momemnt only the popup
-menu)
-
-After connecting to a nvim instance (typically a spawned, embedded instance)
-use the |nvim_ui_attach|(width, height, options) API method to tell nvim that your
-program wants to draw the nvim screen on a grid with "width" times
-"height" cells. "options" should be a dictionary with the following (all
-optional) keys:
- `rgb`: Controls what color format to use.
+GUIs can be implemented as external processes communicating with Nvim over the
+RPC API. Currently the UI model consists of a terminal-like grid with one
+single, monospace font size. Some elements (UI "widgets") can be drawn
+separately from the grid.
+
+After connecting to Nvim (usually a spawned, embedded instance) use the
+|nvim_ui_attach| API method to tell Nvim that your program wants to draw the
+Nvim screen on a grid of width × height cells. `options` must be
+a dictionary with these (optional) keys:
+ `rgb` Controls what color format to use.
Set to true (default) to use 24-bit rgb
colors.
Set to false to use terminal color codes (at
most 256 different colors).
- `popupmenu_external`: Instead of drawing the completion popupmenu on
+ `popupmenu_external` Instead of drawing the completion popupmenu on
the grid, Nvim will send higher-level events to
the ui and let it draw the popupmenu.
Defaults to false.
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index 7d41162cfc..394e38f6e5 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -2790,21 +2790,17 @@ A jump table for the options with a short description can be found at |Q_op|.
i-ci:ver25-Cursor/lCursor,
r-cr:hor20-Cursor/lCursor,
sm:block-Cursor
- -blinkwait175-blinkoff150-blinkon175",
- for Windows console:
- "n-v-c:block,o:hor50,i-ci:hor15,
- r-cr:hor30,sm:block")
- global
- {only available when compiled with GUI enabled, and
- for Windows console}
- This option tells Vim what the cursor should look like in different
- modes. It fully works in the GUI. In a Windows console, only
- the height of the cursor can be changed. This can be done by
- specifying a block cursor, or a percentage for a vertical or
- horizontal cursor.
- For a console the 't_SI' and 't_EI' escape sequences are used.
-
- The option is a comma separated list of parts. Each part consist of a
+ -blinkwait175-blinkoff150-blinkon175")
+ global
+ Configures the cursor style for each mode. Works in the GUI and some
+ terminals. Empty means "non-blinking block cursor in all modes": >
+ :set guicursor=
+<
+ With tmux you might need this in ~/.tmux.conf (see terminal-overrides
+ in the tmux(1) manual page): >
+ set -ga terminal-overrides ',*:Ss=\E[%p1%d q:Se=\E[2 q'
+<
+ The option is a comma separated list of parts. Each part consists of a
mode-list and an argument-list:
mode-list:argument-list,mode-list:argument-list,..
The mode-list is a dash separated list of these modes:
diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt
index 41c0c41c80..bd43028806 100644
--- a/runtime/doc/vim_diff.txt
+++ b/runtime/doc/vim_diff.txt
@@ -113,6 +113,7 @@ Some `CTRL-SHIFT-...` key chords are distinguished from `CTRL-...` variants
Options:
'cpoptions' flags: |cpo-_|
+ 'guicursor' works in the terminal
'inccommand' shows interactive results for |:substitute|-like commands
'statusline' supports unlimited alignment sections
'tabline' %@Func@foo%X can call any function on mouse-click
diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c
index 625bcc6b4b..a95be0fabb 100644
--- a/src/nvim/api/ui.c
+++ b/src/nvim/api/ui.c
@@ -12,6 +12,7 @@
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/popupmnu.h"
+#include "nvim/cursor_shape.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/ui.c.generated.h"
@@ -69,6 +70,7 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height,
ui->clear = remote_ui_clear;
ui->eol_clear = remote_ui_eol_clear;
ui->cursor_goto = remote_ui_cursor_goto;
+ ui->cursor_style_set = remote_ui_cursor_style_set;
ui->update_menu = remote_ui_update_menu;
ui->busy_start = remote_ui_busy_start;
ui->busy_stop = remote_ui_busy_stop;
@@ -298,6 +300,14 @@ static void remote_ui_scroll(UI *ui, int count)
push_call(ui, "scroll", args);
}
+static void remote_ui_cursor_style_set(UI *ui, Dictionary styles)
+{
+ Array args = ARRAY_DICT_INIT;
+ Object copy = copy_object(DICTIONARY_OBJ(styles));
+ ADD(args, copy);
+ push_call(ui, "cursor_style_set", args);
+}
+
static void remote_ui_highlight_set(UI *ui, HlAttrs attrs)
{
Array args = ARRAY_DICT_INIT;
diff --git a/src/nvim/cursor_shape.c b/src/nvim/cursor_shape.c
index b50462664c..7ec70bb724 100644
--- a/src/nvim/cursor_shape.c
+++ b/src/nvim/cursor_shape.c
@@ -7,40 +7,74 @@
#include "nvim/charset.h"
#include "nvim/strings.h"
#include "nvim/syntax.h"
+#include "nvim/api/private/helpers.h"
+#include "nvim/ui.h"
-/*
- * Handling of cursor and mouse pointer shapes in various modes.
- */
-
+/// Handling of cursor and mouse pointer shapes in various modes.
static cursorentry_T shape_table[SHAPE_IDX_COUNT] =
{
- /* The values will be filled in from the 'guicursor' and 'mouseshape'
- * defaults when Vim starts.
- * Adjust the SHAPE_IDX_ defines when making changes! */
- {0, 0, 0, 700L, 400L, 250L, 0, 0, "n", SHAPE_CURSOR+SHAPE_MOUSE},
- {0, 0, 0, 700L, 400L, 250L, 0, 0, "v", SHAPE_CURSOR+SHAPE_MOUSE},
- {0, 0, 0, 700L, 400L, 250L, 0, 0, "i", SHAPE_CURSOR+SHAPE_MOUSE},
- {0, 0, 0, 700L, 400L, 250L, 0, 0, "r", SHAPE_CURSOR+SHAPE_MOUSE},
- {0, 0, 0, 700L, 400L, 250L, 0, 0, "c", SHAPE_CURSOR+SHAPE_MOUSE},
- {0, 0, 0, 700L, 400L, 250L, 0, 0, "ci", SHAPE_CURSOR+SHAPE_MOUSE},
- {0, 0, 0, 700L, 400L, 250L, 0, 0, "cr", SHAPE_CURSOR+SHAPE_MOUSE},
- {0, 0, 0, 700L, 400L, 250L, 0, 0, "o", SHAPE_CURSOR+SHAPE_MOUSE},
- {0, 0, 0, 700L, 400L, 250L, 0, 0, "ve", SHAPE_CURSOR+SHAPE_MOUSE},
- {0, 0, 0, 0L, 0L, 0L, 0, 0, "e", SHAPE_MOUSE},
- {0, 0, 0, 0L, 0L, 0L, 0, 0, "s", SHAPE_MOUSE},
- {0, 0, 0, 0L, 0L, 0L, 0, 0, "sd", SHAPE_MOUSE},
- {0, 0, 0, 0L, 0L, 0L, 0, 0, "vs", SHAPE_MOUSE},
- {0, 0, 0, 0L, 0L, 0L, 0, 0, "vd", SHAPE_MOUSE},
- {0, 0, 0, 0L, 0L, 0L, 0, 0, "m", SHAPE_MOUSE},
- {0, 0, 0, 0L, 0L, 0L, 0, 0, "ml", SHAPE_MOUSE},
- {0, 0, 0, 100L, 100L, 100L, 0, 0, "sm", SHAPE_CURSOR},
+ // Values are set by 'guicursor' and 'mouseshape'.
+ // Adjust the SHAPE_IDX_ defines when changing this!
+ { "normal", 0, 0, 0, 700L, 400L, 250L, 0, 0, "n", SHAPE_CURSOR+SHAPE_MOUSE },
+ { "visual", 0, 0, 0, 700L, 400L, 250L, 0, 0, "v", SHAPE_CURSOR+SHAPE_MOUSE },
+ { "insert", 0, 0, 0, 700L, 400L, 250L, 0, 0, "i", SHAPE_CURSOR+SHAPE_MOUSE },
+ { "replace", 0, 0, 0, 700L, 400L, 250L, 0, 0, "r", SHAPE_CURSOR+SHAPE_MOUSE },
+ { "cmdline_normal", 0, 0, 0, 700L, 400L, 250L, 0, 0, "c", SHAPE_CURSOR+SHAPE_MOUSE },
+ { "cmdline_insert", 0, 0, 0, 700L, 400L, 250L, 0, 0, "ci", SHAPE_CURSOR+SHAPE_MOUSE },
+ { "cmdline_replace", 0, 0, 0, 700L, 400L, 250L, 0, 0, "cr", SHAPE_CURSOR+SHAPE_MOUSE },
+ { "operator", 0, 0, 0, 700L, 400L, 250L, 0, 0, "o", SHAPE_CURSOR+SHAPE_MOUSE },
+ { "visual_select", 0, 0, 0, 700L, 400L, 250L, 0, 0, "ve", SHAPE_CURSOR+SHAPE_MOUSE },
+ { "cmdline_hover", 0, 0, 0, 0L, 0L, 0L, 0, 0, "e", SHAPE_MOUSE },
+ { "statusline_hover", 0, 0, 0, 0L, 0L, 0L, 0, 0, "s", SHAPE_MOUSE },
+ { "statusline_drag", 0, 0, 0, 0L, 0L, 0L, 0, 0, "sd", SHAPE_MOUSE },
+ { "vsep_hover", 0, 0, 0, 0L, 0L, 0L, 0, 0, "vs", SHAPE_MOUSE },
+ { "vsep_drag", 0, 0, 0, 0L, 0L, 0L, 0, 0, "vd", SHAPE_MOUSE },
+ { "more", 0, 0, 0, 0L, 0L, 0L, 0, 0, "m", SHAPE_MOUSE },
+ { "more_lastline", 0, 0, 0, 0L, 0L, 0L, 0, 0, "ml", SHAPE_MOUSE },
+ { "showmatch", 0, 0, 0, 100L, 100L, 100L, 0, 0, "sm", SHAPE_CURSOR },
};
-/*
- * Parse the 'guicursor' option ("what" is SHAPE_CURSOR) or 'mouseshape'
- * ("what" is SHAPE_MOUSE).
- * Returns error message for an illegal option, NULL otherwise.
- */
+/// Converts cursor_shapes into a Dictionary of dictionaries
+/// @return dictionary of the form {"normal" : { "cursor_shape": ... }, ...}
+Dictionary cursor_shape_dict(void)
+{
+ Dictionary all = ARRAY_DICT_INIT;
+
+ for (int i = 0; i < SHAPE_IDX_COUNT; i++) {
+ Dictionary dic = ARRAY_DICT_INIT;
+ cursorentry_T *cur = &shape_table[i];
+ if (cur->used_for & SHAPE_MOUSE) {
+ PUT(dic, "mouse_shape", INTEGER_OBJ(cur->mshape));
+ }
+ if (cur->used_for & SHAPE_CURSOR) {
+ String shape_str;
+ switch (cur->shape) {
+ case SHAPE_BLOCK: shape_str = cstr_to_string("block"); break;
+ case SHAPE_VER: shape_str = cstr_to_string("vertical"); break;
+ case SHAPE_HOR: shape_str = cstr_to_string("horizontal"); break;
+ default: shape_str = cstr_to_string("unknown");
+ }
+ PUT(dic, "cursor_shape", STRING_OBJ(shape_str));
+ PUT(dic, "cell_percentage", INTEGER_OBJ(cur->percentage));
+ PUT(dic, "blinkwait", INTEGER_OBJ(cur->blinkwait));
+ PUT(dic, "blinkon", INTEGER_OBJ(cur->blinkon));
+ PUT(dic, "blinkoff", INTEGER_OBJ(cur->blinkoff));
+ PUT(dic, "hl_id", INTEGER_OBJ(cur->id));
+ PUT(dic, "id_lm", INTEGER_OBJ(cur->id_lm));
+ }
+ PUT(dic, "short_name", STRING_OBJ(cstr_to_string(cur->name)));
+
+ PUT(all, cur->full_name, DICTIONARY_OBJ(dic));
+ }
+
+ return all;
+}
+
+/// Parse the 'guicursor' option
+///
+/// @param what SHAPE_CURSOR or SHAPE_MOUSE ('mouseshape')
+///
+/// @returns error message for an illegal option, NULL otherwise.
char_u *parse_shape_opt(int what)
{
char_u *modep;
@@ -59,10 +93,11 @@ char_u *parse_shape_opt(int what)
* First round: check for errors; second round: do it for real.
*/
for (round = 1; round <= 2; ++round) {
- /*
- * Repeat for all comma separated parts.
- */
+ // Repeat for all comma separated parts.
modep = p_guicursor;
+ if (*p_guicursor == NUL) {
+ modep = (char_u *)"a:block-blinkon0";
+ }
while (*modep != NUL) {
colonp = vim_strchr(modep, ':');
if (colonp == NULL)
@@ -71,19 +106,18 @@ char_u *parse_shape_opt(int what)
return (char_u *)N_("E546: Illegal mode");
commap = vim_strchr(modep, ',');
- /*
- * Repeat for all mode's before the colon.
- * For the 'a' mode, we loop to handle all the modes.
- */
+ // Repeat for all modes before the colon.
+ // For the 'a' mode, we loop to handle all the modes.
all_idx = -1;
assert(modep < colonp);
while (modep < colonp || all_idx >= 0) {
if (all_idx < 0) {
- /* Find the mode. */
- if (modep[1] == '-' || modep[1] == ':')
+ // Find the mode
+ if (modep[1] == '-' || modep[1] == ':') {
len = 1;
- else
+ } else {
len = 2;
+ }
if (len == 1 && TOLOWER_ASC(modep[0]) == 'a') {
all_idx = SHAPE_IDX_COUNT - 1;
@@ -100,11 +134,11 @@ char_u *parse_shape_opt(int what)
modep += len + 1;
}
- if (all_idx >= 0)
+ if (all_idx >= 0) {
idx = all_idx--;
- else if (round == 2) {
+ } else if (round == 2) {
{
- /* Set the defaults, for the missing parts */
+ // Set the defaults, for the missing parts
shape_table[idx].shape = SHAPE_BLOCK;
shape_table[idx].blinkwait = 700L;
shape_table[idx].blinkon = 400L;
@@ -208,6 +242,23 @@ char_u *parse_shape_opt(int what)
shape_table[SHAPE_IDX_VE].id_lm = shape_table[SHAPE_IDX_V].id_lm;
}
}
-
+ ui_cursor_style_set();
return NULL;
}
+
+
+/// Map cursor mode from string to integer
+///
+/// @param mode Fullname of the mode whose id we are looking for
+/// @return -1 in case of failure, else the matching SHAPE_ID* integer
+int cursor_mode_str2int(const char *mode)
+{
+ for (int current_mode = 0; current_mode < SHAPE_IDX_COUNT; current_mode++) {
+ if (strcmp(shape_table[current_mode].full_name, mode) == 0) {
+ return current_mode;
+ }
+ }
+ ELOG("Unknown mode %s", mode);
+ return -1;
+}
+
diff --git a/src/nvim/cursor_shape.h b/src/nvim/cursor_shape.h
index 9ce1b6e0a0..0006ede31d 100644
--- a/src/nvim/cursor_shape.h
+++ b/src/nvim/cursor_shape.h
@@ -1,32 +1,34 @@
#ifndef NVIM_CURSOR_SHAPE_H
#define NVIM_CURSOR_SHAPE_H
-/*
- * struct to store values from 'guicursor' and 'mouseshape'
- */
-/* Indexes in shape_table[] */
-#define SHAPE_IDX_N 0 /* Normal mode */
-#define SHAPE_IDX_V 1 /* Visual mode */
-#define SHAPE_IDX_I 2 /* Insert mode */
-#define SHAPE_IDX_R 3 /* Replace mode */
-#define SHAPE_IDX_C 4 /* Command line Normal mode */
-#define SHAPE_IDX_CI 5 /* Command line Insert mode */
-#define SHAPE_IDX_CR 6 /* Command line Replace mode */
-#define SHAPE_IDX_O 7 /* Operator-pending mode */
-#define SHAPE_IDX_VE 8 /* Visual mode with 'selection' exclusive */
-#define SHAPE_IDX_CLINE 9 /* On command line */
-#define SHAPE_IDX_STATUS 10 /* A status line */
-#define SHAPE_IDX_SDRAG 11 /* dragging a status line */
-#define SHAPE_IDX_VSEP 12 /* A vertical separator line */
-#define SHAPE_IDX_VDRAG 13 /* dragging a vertical separator line */
-#define SHAPE_IDX_MORE 14 /* Hit-return or More */
-#define SHAPE_IDX_MOREL 15 /* Hit-return or More in last line */
-#define SHAPE_IDX_SM 16 /* showing matching paren */
-#define SHAPE_IDX_COUNT 17
+/// struct to store values from 'guicursor' and 'mouseshape'
+/// Indexes in shape_table[]
+typedef enum {
+SHAPE_IDX_N = 0, ///< Normal mode
+SHAPE_IDX_V = 1, ///< Visual mode
+SHAPE_IDX_I = 2, ///< Insert mode
+SHAPE_IDX_R = 3, ///< Replace mode
+SHAPE_IDX_C = 4, ///< Command line Normal mode
+SHAPE_IDX_CI = 5, ///< Command line Insert mode
+SHAPE_IDX_CR = 6, ///< Command line Replace mode
+SHAPE_IDX_O = 7, ///< Operator-pending mode
+SHAPE_IDX_VE = 8, ///< Visual mode with 'selection' exclusive
+SHAPE_IDX_CLINE = 9, ///< On command line
+SHAPE_IDX_STATUS = 10, ///< On status line
+SHAPE_IDX_SDRAG = 11, ///< dragging a status line
+SHAPE_IDX_VSEP = 12, ///< On vertical separator line
+SHAPE_IDX_VDRAG = 13, ///< dragging a vertical separator line
+SHAPE_IDX_MORE = 14, ///< Hit-return or More
+SHAPE_IDX_MOREL = 15, ///< Hit-return or More in last line
+SHAPE_IDX_SM = 16, ///< showing matching paren
+SHAPE_IDX_COUNT = 17
+} MouseMode;
-#define SHAPE_BLOCK 0 /* block cursor */
-#define SHAPE_HOR 1 /* horizontal bar cursor */
-#define SHAPE_VER 2 /* vertical bar cursor */
+typedef enum {
+SHAPE_BLOCK = 0, ///< block cursor
+SHAPE_HOR = 1, ///< horizontal bar cursor
+SHAPE_VER = 2 ///< vertical bar cursor
+} CursorShape;
#define MSHAPE_NUMBERED 1000 /* offset for shapes identified by number */
#define MSHAPE_HIDE 1 /* hide mouse pointer */
@@ -35,16 +37,17 @@
#define SHAPE_CURSOR 2 /* used for text cursor shape */
typedef struct cursor_entry {
- int shape; /* one of the SHAPE_ defines */
- int mshape; /* one of the MSHAPE defines */
- int percentage; /* percentage of cell for bar */
- long blinkwait; /* blinking, wait time before blinking starts */
- long blinkon; /* blinking, on time */
- long blinkoff; /* blinking, off time */
- int id; /* highlight group ID */
- int id_lm; /* highlight group ID for :lmap mode */
- char *name; /* mode name (fixed) */
- char used_for; /* SHAPE_MOUSE and/or SHAPE_CURSOR */
+ char *full_name; ///< mode description
+ CursorShape shape; ///< cursor shape: one of the SHAPE_ defines
+ int mshape; ///< mouse shape: one of the MSHAPE defines
+ int percentage; ///< percentage of cell for bar
+ long blinkwait; ///< blinking, wait time before blinking starts
+ long blinkon; ///< blinking, on time
+ long blinkoff; ///< blinking, off time
+ int id; ///< highlight group ID
+ int id_lm; ///< highlight group ID for :lmap mode
+ char *name; ///< mode short name
+ char used_for; ///< SHAPE_MOUSE and/or SHAPE_CURSOR
} cursorentry_T;
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index 9dff3410d6..09f016cf5a 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -1000,7 +1000,7 @@ return {
deny_duplicates=true,
vi_def=true,
varname='p_guicursor',
- defaults={if_true={vi="n-v-c:block,o:hor50,i-ci:hor15,r-cr:hor30,sm:block"}}
+ defaults={if_true={vi="n-v-c:block-Cursor/lCursor,ve:ver35-Cursor,o:hor50-Cursor,i-ci:ver25-Cursor/lCursor,r-cr:hor20-Cursor/lCursor,sm:block-Cursor-blinkwait175-blinkoff150-blinkon175"}}
},
{
full_name='guifont', abbreviation='gfn',
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index 4a7b4a0eac..acda25e738 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -42,34 +42,38 @@
static bool did_syntax_onoff = false;
-// Structure that stores information about a highlight group.
-// The ID of a highlight group is also called group ID. It is the index in
-// the highlight_ga array PLUS ONE.
+/// Structure that stores information about a highlight group.
+/// The ID of a highlight group is also called group ID. It is the index in
+/// the highlight_ga array PLUS ONE.
struct hl_group {
- char_u *sg_name; // highlight group name
- char_u *sg_name_u; // uppercase of sg_name
- int sg_attr; // Screen attr
- int sg_link; // link to this highlight group ID
- int sg_set; // combination of SG_* flags
- scid_T sg_scriptID; // script in which the group was last set
+ char_u *sg_name; ///< highlight group name
+ char_u *sg_name_u; ///< uppercase of sg_name
+ int sg_attr; ///< Screen attr @see ATTR_ENTRY
+ int sg_link; ///< link to this highlight group ID
+ int sg_set; ///< combination of flags in \ref SG_SET
+ scid_T sg_scriptID; ///< script in which the group was last set
// for terminal UIs
- int sg_cterm; // "cterm=" highlighting attr
- int sg_cterm_fg; // terminal fg color number + 1
- int sg_cterm_bg; // terminal bg color number + 1
- int sg_cterm_bold; // bold attr was set for light color
+ int sg_cterm; ///< "cterm=" highlighting attr
+ int sg_cterm_fg; ///< terminal fg color number + 1
+ int sg_cterm_bg; ///< terminal bg color number + 1
+ int sg_cterm_bold; ///< bold attr was set for light color
// for RGB UIs
- int sg_gui; // "gui=" highlighting attributes
- RgbValue sg_rgb_fg; // RGB foreground color
- RgbValue sg_rgb_bg; // RGB background color
- RgbValue sg_rgb_sp; // RGB special color
- uint8_t *sg_rgb_fg_name; // RGB foreground color name
- uint8_t *sg_rgb_bg_name; // RGB background color name
- uint8_t *sg_rgb_sp_name; // RGB special color name
+ int sg_gui; ///< "gui=" highlighting attributes
+ ///< (combination of \ref HL_ATTRIBUTES)
+ RgbValue sg_rgb_fg; ///< RGB foreground color
+ RgbValue sg_rgb_bg; ///< RGB background color
+ RgbValue sg_rgb_sp; ///< RGB special color
+ uint8_t *sg_rgb_fg_name; ///< RGB foreground color name
+ uint8_t *sg_rgb_bg_name; ///< RGB background color name
+ uint8_t *sg_rgb_sp_name; ///< RGB special color name
};
+/// \addtogroup SG_SET
+/// @{
#define SG_CTERM 2 // cterm has been set
#define SG_GUI 4 // gui has been set
#define SG_LINK 8 // link has been set
+/// @}
// highlight groups for 'highlight' option
static garray_T highlight_ga = GA_EMPTY_INIT_VALUE;
@@ -6093,16 +6097,16 @@ int load_colors(char_u *name)
return retval;
}
-/*
- * Handle the ":highlight .." command.
- * When using ":hi clear" this is called recursively for each group with
- * "forceit" and "init" both TRUE.
- */
-void
-do_highlight (
+
+/// Handle the ":highlight .." command.
+/// When using ":hi clear" this is called recursively for each group with
+/// "forceit" and "init" both TRUE.
+/// @param init TRUE when called for initializing
+void
+do_highlight(
char_u *line,
int forceit,
- int init /* TRUE when called for initializing */
+ int init
)
{
char_u *name_end;
@@ -6704,12 +6708,10 @@ static garray_T attr_table = GA_EMPTY_INIT_VALUE;
#define ATTR_ENTRY(idx) ((attrentry_T *)attr_table.ga_data)[idx]
-/*
- * Return the attr number for a set of colors and font.
- * Add a new entry to the term_attr_table, attr_table or gui_attr_table
- * if the combination is new.
- * Return 0 for error.
- */
+/// Return the attr number for a set of colors and font.
+/// Add a new entry to the term_attr_table, attr_table or gui_attr_table
+/// if the combination is new.
+/// @return 0 for error.
int get_attr_entry(attrentry_T *aep)
{
garray_T *table = &attr_table;
@@ -6932,7 +6934,7 @@ static int highlight_list_arg(int id, int didh, int type, int iarg, char_u *sarg
/// Check whether highlight group has attribute
///
-/// @param[in] id Highilght group to check.
+/// @param[in] id Highlight group to check.
/// @param[in] flag Attribute to check.
/// @param[in] modec 'g' for GUI, 'c' for term.
///
@@ -7165,12 +7167,13 @@ int syn_namen2id(char_u *linep, int len)
return id;
}
-/*
- * Find highlight group name in the table and return it's ID.
- * The argument is a pointer to the name and the length of the name.
- * If it doesn't exist yet, a new entry is created.
- * Return 0 for failure.
- */
+/// Find highlight group name in the table and return it's ID.
+/// If it doesn't exist yet, a new entry is created.
+///
+/// @param pp Highlight group name
+/// @param len length of \p pp
+///
+/// @return 0 for failure else the id of the group
int syn_check_group(char_u *pp, int len)
{
char_u *name = vim_strnsave(pp, len);
@@ -8244,7 +8247,14 @@ color_name_table_T color_name_table[] = {
{ NULL, 0 },
};
-RgbValue name_to_color(uint8_t *name)
+
+/// Translate to RgbValue if \p name is an hex value (e.g. #XXXXXX),
+/// else look into color_name_table to translate a color name to its
+/// hex value
+///
+/// @param[in] name string value to convert to RGB
+/// return the hex value or -1 if could not find a correct value
+RgbValue name_to_color(const uint8_t *name)
{
if (name[0] == '#' && isxdigit(name[1]) && isxdigit(name[2])
diff --git a/src/nvim/syntax.h b/src/nvim/syntax.h
index af2ac719c6..574e3372e2 100644
--- a/src/nvim/syntax.h
+++ b/src/nvim/syntax.h
@@ -5,10 +5,11 @@
#include "nvim/buffer_defs.h"
-/*
- * Terminal highlighting attribute bits.
- * Attributes above HL_ALL are used for syntax highlighting.
- */
+
+/// Terminal highlighting attribute bits.
+/// Attributes above HL_ALL are used for syntax highlighting.
+/// \addtogroup HL_ATTRIBUTES
+/// @{
#define HL_NORMAL 0x00
#define HL_INVERSE 0x01
#define HL_BOLD 0x02
@@ -16,6 +17,7 @@
#define HL_UNDERLINE 0x08
#define HL_UNDERCURL 0x10
#define HL_STANDOUT 0x20
+/// @}
#define HL_CONTAINED 0x01 /* not used on toplevel */
#define HL_TRANSP 0x02 /* has no highlighting */
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index ebdfb1e7a1..badc0cd870 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -31,6 +31,8 @@
#include "nvim/ugrid.h"
#include "nvim/tui/input.h"
#include "nvim/tui/tui.h"
+#include "nvim/cursor_shape.h"
+#include "nvim/syntax.h"
// Space reserved in the output buffer to restore the cursor to normal when
// flushing. No existing terminal will require 32 bytes to do that.
@@ -69,13 +71,14 @@ typedef struct {
bool can_use_terminal_scroll;
bool mouse_enabled;
bool busy;
+ cursorentry_T cursor_shapes[SHAPE_IDX_COUNT];
HlAttrs print_attrs;
int showing_mode;
struct {
int enable_mouse, disable_mouse;
int enable_bracketed_paste, disable_bracketed_paste;
- int set_cursor_shape_bar, set_cursor_shape_ul, set_cursor_shape_block;
int set_rgb_foreground, set_rgb_background;
+ int set_cursor_color;
int enable_focus_reporting, disable_focus_reporting;
} unibi_ext;
} TUIData;
@@ -97,6 +100,7 @@ UI *tui_start(void)
ui->clear = tui_clear;
ui->eol_clear = tui_eol_clear;
ui->cursor_goto = tui_cursor_goto;
+ ui->cursor_style_set = tui_cursor_style_set;
ui->update_menu = tui_update_menu;
ui->busy_start = tui_busy_start;
ui->busy_stop = tui_busy_stop;
@@ -129,11 +133,9 @@ static void terminfo_start(UI *ui)
data->showing_mode = 0;
data->unibi_ext.enable_mouse = -1;
data->unibi_ext.disable_mouse = -1;
+ data->unibi_ext.set_cursor_color = -1;
data->unibi_ext.enable_bracketed_paste = -1;
data->unibi_ext.disable_bracketed_paste = -1;
- data->unibi_ext.set_cursor_shape_bar = -1;
- data->unibi_ext.set_cursor_shape_ul = -1;
- data->unibi_ext.set_cursor_shape_block = -1;
data->unibi_ext.enable_focus_reporting = -1;
data->unibi_ext.disable_focus_reporting = -1;
data->out_fd = 1;
@@ -146,8 +148,6 @@ static void terminfo_start(UI *ui)
data->ut = unibi_dummy();
}
fix_terminfo(data);
- // Initialize the cursor shape.
- unibi_out(ui, data->unibi_ext.set_cursor_shape_block);
// Set 't_Co' from the result of unibilium & fix_terminfo.
t_colors = unibi_get_num(data->ut, unibi_max_colors);
// Enter alternate screen and clear
@@ -434,6 +434,64 @@ static void tui_cursor_goto(UI *ui, int row, int col)
unibi_goto(ui, row, col);
}
+CursorShape tui_cursor_decode_shape(const char *shape_str)
+{
+ CursorShape shape = 0;
+ if (strcmp(shape_str, "block") == 0) {
+ shape = SHAPE_BLOCK;
+ } else if (strcmp(shape_str, "vertical") == 0) {
+ shape = SHAPE_VER;
+ } else if (strcmp(shape_str, "horizontal") == 0) {
+ shape = SHAPE_HOR;
+ } else {
+ EMSG2(_(e_invarg2), shape_str);
+ }
+ return shape;
+}
+
+static cursorentry_T decode_cursor_entry(Dictionary args)
+{
+ cursorentry_T r;
+
+ for (size_t i = 0; i < args.size; i++) {
+ char *keyStr = args.items[i].key.data;
+ Object value = args.items[i].value;
+
+ if (strcmp(keyStr, "cursor_shape") == 0) {
+ r.shape = tui_cursor_decode_shape(args.items[i].value.data.string.data);
+ } else if (strcmp(keyStr, "blinkon") == 0) {
+ r.blinkon = (int)value.data.integer;
+ } else if (strcmp(keyStr, "blinkoff") == 0) {
+ r.blinkoff = (int)value.data.integer;
+ } else if (strcmp(keyStr, "hl_id") == 0) {
+ r.id = (int)value.data.integer;
+ }
+ }
+ return r;
+}
+
+static void tui_cursor_style_set(UI *ui, Dictionary args)
+{
+ TUIData *data = ui->data;
+
+ for (size_t i = 0; i < args.size; i++) {
+ char *mode_name = args.items[i].key.data;
+ const int mode_id = cursor_mode_str2int(mode_name);
+
+ if (mode_id < 0) {
+ WLOG("Unknown mode '%s'", mode_name);
+ continue;
+ }
+ cursorentry_T r = decode_cursor_entry(args.items[i].value.data.dictionary);
+ r.full_name = mode_name;
+ data->cursor_shapes[mode_id] = r;
+ }
+
+ // force redraw
+ MouseMode cursor_mode = tui_mode2cursor(data->showing_mode);
+ tui_set_cursor(ui, cursor_mode);
+}
+
static void tui_update_menu(UI *ui)
{
// Do nothing; menus are for GUI only
@@ -467,33 +525,101 @@ static void tui_mouse_off(UI *ui)
}
}
+/// @param mode one of SHAPE_XXX
+static void tui_set_cursor(UI *ui, MouseMode mode)
+{
+ TUIData *data = ui->data;
+ cursorentry_T c = data->cursor_shapes[mode];
+ int shape = c.shape;
+ bool inside_tmux = os_getenv("TMUX") != NULL;
+ unibi_var_t vars[26 + 26] = { { 0 } };
+
+# define TMUX_WRAP(seq) (inside_tmux ? "\x1bPtmux;\x1b" seq "\x1b\\" : seq)
+ // Support changing cursor shape on some popular terminals.
+ const char *term_prog = os_getenv("TERM_PROGRAM");
+ const char *vte_version = os_getenv("VTE_VERSION");
+
+ if ((term_prog && !strcmp(term_prog, "Konsole"))
+ || os_getenv("KONSOLE_DBUS_SESSION") != NULL) {
+ // Konsole uses a proprietary escape code to set the cursor shape
+ // and does not support DECSCUSR.
+ switch (shape) {
+ case SHAPE_BLOCK: shape = 0; break;
+ case SHAPE_VER: shape = 1; break;
+ case SHAPE_HOR: shape = 3; break;
+ default: WLOG("Unknown shape value %d", shape); break;
+ }
+ data->params[0].i = shape;
+ data->params[1].i = (c.blinkon == 0);
+
+ unibi_format(vars, vars + 26,
+ TMUX_WRAP("\x1b]50;CursorShape=%p1%d;BlinkingCursorEnabled=%p2%d\x07"),
+ data->params, out, ui, NULL, NULL);
+ } else if (!vte_version || atoi(vte_version) >= 3900) {
+ // Assume that the terminal supports DECSCUSR unless it is an
+ // old VTE based terminal. This should not get wrapped for tmux,
+ // which will handle it via its Ss/Se terminfo extension - usually
+ // according to its terminal-overrides.
+
+ switch (shape) {
+ case SHAPE_BLOCK: shape = 1; break;
+ case SHAPE_VER: shape = 5; break;
+ case SHAPE_HOR: shape = 3; break;
+ default: WLOG("Unknown shape value %d", shape); break;
+ }
+ data->params[0].i = shape + (c.blinkon ==0);
+ unibi_format(vars, vars + 26, "\x1b[%p1%d q",
+ data->params, out, ui, NULL, NULL);
+ }
+
+ if (c.id != 0 && ui->rgb) {
+ int attr = syn_id2attr(c.id);
+ attrentry_T *aep = syn_cterm_attr2entry(attr);
+ data->params[0].i = aep->rgb_bg_color;
+ unibi_out(ui, data->unibi_ext.set_cursor_color);
+ }
+}
+
+/// Returns cursor mode from edit mode
+static MouseMode tui_mode2cursor(int mode)
+{
+ switch (mode) {
+ case INSERT: return SHAPE_IDX_I;
+ case CMDLINE: return SHAPE_IDX_C;
+ case REPLACE: return SHAPE_IDX_R;
+ case NORMAL:
+ default: return SHAPE_IDX_N;
+ }
+}
+
+/// @param mode editor mode
static void tui_mode_change(UI *ui, int mode)
{
TUIData *data = ui->data;
if (mode == INSERT) {
if (data->showing_mode != INSERT) {
- unibi_out(ui, data->unibi_ext.set_cursor_shape_bar);
+ tui_set_cursor(ui, SHAPE_IDX_I);
}
} else if (mode == CMDLINE) {
if (data->showing_mode != CMDLINE) {
- unibi_out(ui, data->unibi_ext.set_cursor_shape_bar);
+ tui_set_cursor(ui, SHAPE_IDX_C);
}
} else if (mode == REPLACE) {
if (data->showing_mode != REPLACE) {
- unibi_out(ui, data->unibi_ext.set_cursor_shape_ul);
+ tui_set_cursor(ui, SHAPE_IDX_R);
}
} else {
assert(mode == NORMAL);
if (data->showing_mode != NORMAL) {
- unibi_out(ui, data->unibi_ext.set_cursor_shape_block);
+ tui_set_cursor(ui, SHAPE_IDX_N);
}
}
data->showing_mode = mode;
}
static void tui_set_scroll_region(UI *ui, int top, int bot, int left,
- int right)
+ int right)
{
TUIData *data = ui->data;
ugrid_set_scroll_region(&data->grid, top, bot, left, right);
@@ -831,8 +957,6 @@ static void fix_terminfo(TUIData *data)
goto end;
}
- bool inside_tmux = os_getenv("TMUX") != NULL;
-
#define STARTS_WITH(str, prefix) (!memcmp(str, prefix, sizeof(prefix) - 1))
if (STARTS_WITH(term, "rxvt")) {
@@ -890,42 +1014,10 @@ static void fix_terminfo(TUIData *data)
unibi_set_str(ut, unibi_set_a_background, XTERM_SETAB);
}
- const char * env_cusr_shape = os_getenv("NVIM_TUI_ENABLE_CURSOR_SHAPE");
- if (env_cusr_shape && strncmp(env_cusr_shape, "0", 1) == 0) {
- goto end;
- }
- bool cusr_blink = env_cusr_shape && strncmp(env_cusr_shape, "2", 1) == 0;
-
-#define TMUX_WRAP(seq) (inside_tmux ? "\x1bPtmux;\x1b" seq "\x1b\\" : seq)
- // Support changing cursor shape on some popular terminals.
- const char *term_prog = os_getenv("TERM_PROGRAM");
- const char *vte_version = os_getenv("VTE_VERSION");
-
- if ((term_prog && !strcmp(term_prog, "Konsole"))
- || os_getenv("KONSOLE_DBUS_SESSION") != NULL) {
- // Konsole uses a proprietary escape code to set the cursor shape
- // and does not support DECSCUSR.
- data->unibi_ext.set_cursor_shape_bar = (int)unibi_add_ext_str(ut, NULL,
- TMUX_WRAP("\x1b]50;CursorShape=1\x07"));
- data->unibi_ext.set_cursor_shape_ul = (int)unibi_add_ext_str(ut, NULL,
- TMUX_WRAP("\x1b]50;CursorShape=2\x07"));
- data->unibi_ext.set_cursor_shape_block = (int)unibi_add_ext_str(ut, NULL,
- TMUX_WRAP("\x1b]50;CursorShape=0\x07"));
- } else if (!vte_version || atoi(vte_version) >= 3900) {
- // Assume that the terminal supports DECSCUSR unless it is an
- // old VTE based terminal. This should not get wrapped for tmux,
- // which will handle it via its Ss/Se terminfo extension - usually
- // according to its terminal-overrides.
- data->unibi_ext.set_cursor_shape_bar =
- (int)unibi_add_ext_str(ut, NULL, cusr_blink ? "\x1b[5 q" : "\x1b[6 q");
- data->unibi_ext.set_cursor_shape_ul =
- (int)unibi_add_ext_str(ut, NULL, cusr_blink ? "\x1b[3 q" : "\x1b[4 q");
- data->unibi_ext.set_cursor_shape_block =
- (int)unibi_add_ext_str(ut, NULL, cusr_blink ? "\x1b[1 q" : "\x1b[2 q");
- }
-
end:
// Fill some empty slots with common terminal strings
+ data->unibi_ext.set_cursor_color = (int)unibi_add_ext_str(
+ ut, NULL, "\033]12;#%p1%06x\007");
data->unibi_ext.enable_mouse = (int)unibi_add_ext_str(ut, NULL,
"\x1b[?1002h\x1b[?1006h");
data->unibi_ext.disable_mouse = (int)unibi_add_ext_str(ut, NULL,
diff --git a/src/nvim/tui/tui.h b/src/nvim/tui/tui.h
index 07523bc124..2915b0e2f8 100644
--- a/src/nvim/tui/tui.h
+++ b/src/nvim/tui/tui.h
@@ -1,6 +1,8 @@
#ifndef NVIM_TUI_TUI_H
#define NVIM_TUI_TUI_H
+#include "nvim/cursor_shape.h"
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "tui/tui.h.generated.h"
#endif
diff --git a/src/nvim/ui.c b/src/nvim/ui.c
index ea42e3e357..babb4efa96 100644
--- a/src/nvim/ui.c
+++ b/src/nvim/ui.c
@@ -29,6 +29,7 @@
#include "nvim/screen.h"
#include "nvim/syntax.h"
#include "nvim/window.h"
+#include "nvim/cursor_shape.h"
#ifdef FEAT_TUI
# include "nvim/tui/tui.h"
#else
@@ -179,6 +180,7 @@ void ui_refresh(void)
row = col = 0;
screen_resize(width, height);
pum_set_external(pum_external);
+ ui_cursor_style_set();
}
static void ui_refresh_event(void **argv)
@@ -376,6 +378,13 @@ void ui_cursor_goto(int new_row, int new_col)
pending_cursor_update = true;
}
+void ui_cursor_style_set(void)
+{
+ Dictionary style = cursor_shape_dict();
+ UI_CALL(cursor_style_set, style);
+ api_free_dictionary(style);
+}
+
void ui_update_menu(void)
{
UI_CALL(update_menu);
diff --git a/src/nvim/ui.h b/src/nvim/ui.h
index d14bc5812c..0af0c0db65 100644
--- a/src/nvim/ui.h
+++ b/src/nvim/ui.h
@@ -22,6 +22,7 @@ struct ui_t {
void (*clear)(UI *ui);
void (*eol_clear)(UI *ui);
void (*cursor_goto)(UI *ui, int row, int col);
+ void (*cursor_style_set)(UI *ui, Dictionary cursor_shapes);
void (*update_menu)(UI *ui);
void (*busy_start)(UI *ui);
void (*busy_stop)(UI *ui);
diff --git a/src/nvim/ui_bridge.c b/src/nvim/ui_bridge.c
index 25861abc1b..c9bad6b254 100644
--- a/src/nvim/ui_bridge.c
+++ b/src/nvim/ui_bridge.c
@@ -13,6 +13,7 @@
#include "nvim/memory.h"
#include "nvim/ui_bridge.h"
#include "nvim/ugrid.h"
+#include "nvim/api/private/helpers.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ui_bridge.c.generated.h"
@@ -59,6 +60,7 @@ UI *ui_bridge_attach(UI *ui, ui_main_fn ui_main, event_scheduler scheduler)
rv->bridge.clear = ui_bridge_clear;
rv->bridge.eol_clear = ui_bridge_eol_clear;
rv->bridge.cursor_goto = ui_bridge_cursor_goto;
+ rv->bridge.cursor_style_set = ui_bridge_cursor_styleset;
rv->bridge.update_menu = ui_bridge_update_menu;
rv->bridge.busy_start = ui_bridge_busy_start;
rv->bridge.busy_stop = ui_bridge_busy_stop;
@@ -178,6 +180,23 @@ static void ui_bridge_cursor_goto_event(void **argv)
ui->cursor_goto(ui, PTR2INT(argv[1]), PTR2INT(argv[2]));
}
+static void ui_bridge_cursor_styleset(UI *b, Dictionary style)
+{
+ Object copy = copy_object(DICTIONARY_OBJ(style));
+ Object *pobj = xmalloc(sizeof(copy));
+ *pobj = copy;
+ UI_CALL(b, cursor_styleset, 2, b, pobj);
+}
+static void ui_bridge_cursor_styleset_event(void **argv)
+{
+ UI *ui = UI(argv[0]);
+ Object *styles = (Object *)argv[1];
+
+ ui->cursor_style_set(ui, styles->data.dictionary);
+ api_free_object(*styles);
+ xfree(styles);
+}
+
static void ui_bridge_update_menu(UI *b)
{
UI_CALL(b, update_menu, 1, b);
diff --git a/test/functional/ui/cursor_spec.lua b/test/functional/ui/cursor_spec.lua
new file mode 100644
index 0000000000..8b42c193ab
--- /dev/null
+++ b/test/functional/ui/cursor_spec.lua
@@ -0,0 +1,182 @@
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+local clear, feed, meths = helpers.clear, helpers.feed, helpers.meths
+local insert, execute = helpers.insert, helpers.execute
+local eq, funcs = helpers.eq, helpers.funcs
+local command = helpers.command
+
+describe('ui/cursor', function()
+ local screen
+
+ before_each(function()
+ clear()
+ screen = Screen.new(25, 5)
+ screen:attach()
+ end)
+
+ after_each(function()
+ screen:detach()
+ end)
+
+ it("'guicursor' is published as a UI event", function()
+ command('redraw')
+ screen:expect('', nil, nil, nil, true) -- Tickle the event-loop.
+ local expected_cursor_style = {
+ cmdline_hover = {
+ mouse_shape = 0,
+ short_name = 'e' },
+ cmdline_insert = {
+ blinkoff = 250,
+ blinkon = 400,
+ blinkwait = 700,
+ cell_percentage = 25,
+ cursor_shape = 'vertical',
+ hl_id = 45,
+ id_lm = 46,
+ mouse_shape = 0,
+ short_name = 'ci' },
+ cmdline_normal = {
+ blinkoff = 250,
+ blinkon = 400,
+ blinkwait = 700,
+ cell_percentage = 0,
+ cursor_shape = 'block',
+ hl_id = 45,
+ id_lm = 46,
+ mouse_shape = 0,
+ short_name = 'c' },
+ cmdline_replace = {
+ blinkoff = 250,
+ blinkon = 400,
+ blinkwait = 700,
+ cell_percentage = 20,
+ cursor_shape = 'horizontal',
+ hl_id = 45,
+ id_lm = 46,
+ mouse_shape = 0,
+ short_name = 'cr' },
+ insert = {
+ blinkoff = 250,
+ blinkon = 400,
+ blinkwait = 700,
+ cell_percentage = 25,
+ cursor_shape = 'vertical',
+ hl_id = 45,
+ id_lm = 46,
+ mouse_shape = 0,
+ short_name = 'i' },
+ more = {
+ mouse_shape = 0,
+ short_name = 'm' },
+ more_lastline = {
+ mouse_shape = 0,
+ short_name = 'ml' },
+ normal = {
+ blinkoff = 250,
+ blinkon = 400,
+ blinkwait = 700,
+ cell_percentage = 0,
+ cursor_shape = 'block',
+ hl_id = 45,
+ id_lm = 46,
+ mouse_shape = 0,
+ short_name = 'n' },
+ operator = {
+ blinkoff = 250,
+ blinkon = 400,
+ blinkwait = 700,
+ cell_percentage = 50,
+ cursor_shape = 'horizontal',
+ hl_id = 45,
+ id_lm = 45,
+ mouse_shape = 0,
+ short_name = 'o' },
+ replace = {
+ blinkoff = 250,
+ blinkon = 400,
+ blinkwait = 700,
+ cell_percentage = 20,
+ cursor_shape = 'horizontal',
+ hl_id = 45,
+ id_lm = 46,
+ mouse_shape = 0,
+ short_name = 'r' },
+ showmatch = {
+ blinkoff = 150,
+ blinkon = 175,
+ blinkwait = 175,
+ cell_percentage = 0,
+ cursor_shape = 'block',
+ hl_id = 45,
+ id_lm = 45,
+ short_name = 'sm' },
+ statusline_drag = {
+ mouse_shape = 0,
+ short_name = 'sd' },
+ statusline_hover = {
+ mouse_shape = 0,
+ short_name = 's' },
+ visual = {
+ blinkoff = 250,
+ blinkon = 400,
+ blinkwait = 700,
+ cell_percentage = 0,
+ cursor_shape = 'block',
+ hl_id = 45,
+ id_lm = 46,
+ mouse_shape = 0,
+ short_name = 'v' },
+ visual_select = {
+ blinkoff = 250,
+ blinkon = 400,
+ blinkwait = 700,
+ cell_percentage = 35,
+ cursor_shape = 'vertical',
+ hl_id = 45,
+ id_lm = 45,
+ mouse_shape = 0,
+ short_name = 've' },
+ vsep_drag = {
+ mouse_shape = 0,
+ short_name = 'vd' },
+ vsep_hover = {
+ mouse_shape = 0,
+ short_name = 'vs' }
+ }
+ -- Default 'guicursor' published on startup.
+ eq(expected_cursor_style, screen._cursor_style)
+ eq('normal', screen.mode)
+
+ -- Event is published ONLY if the cursor style changed.
+ screen._cursor_style = nil
+ command('redraw')
+ screen:expect('', nil, nil, nil, true) -- Tickle the event-loop.
+ eq(nil, screen._cursor_style)
+
+ -- Change the cursor style.
+ meths.set_option('guicursor', 'n-v-c:ver35-blinkwait171-blinkoff172-blinkon173,ve:hor35,o:ver50,i-ci:block,r-cr:hor90,sm:ver42')
+ command('redraw')
+ screen:expect('', nil, nil, nil, true) -- Tickle the event-loop.
+ eq('vertical', screen._cursor_style.normal.cursor_shape)
+ eq('horizontal', screen._cursor_style.visual_select.cursor_shape)
+ eq('vertical', screen._cursor_style.operator.cursor_shape)
+ eq('block', screen._cursor_style.insert.cursor_shape)
+ eq('vertical', screen._cursor_style.showmatch.cursor_shape)
+ eq(171, screen._cursor_style.normal.blinkwait)
+ eq(172, screen._cursor_style.normal.blinkoff)
+ eq(173, screen._cursor_style.normal.blinkon)
+ end)
+
+ it("empty 'guicursor' sets cursor_shape=block in all modes", function()
+ meths.set_option('guicursor', '')
+ command('redraw')
+ screen:expect('', nil, nil, nil, true) -- Tickle the event-loop.
+ for _, m in ipairs({ 'cmdline_insert', 'cmdline_normal', 'cmdline_replace', 'insert',
+ 'showmatch', 'normal', 'replace', 'visual',
+ 'visual_select', }) do
+ eq('block', screen._cursor_style[m].cursor_shape)
+ eq(0, screen._cursor_style[m].blinkon)
+ end
+ end)
+
+end)
diff --git a/test/functional/ui/mouse_spec.lua b/test/functional/ui/mouse_spec.lua
index b2fbedfb5e..ecbd5642d1 100644
--- a/test/functional/ui/mouse_spec.lua
+++ b/test/functional/ui/mouse_spec.lua
@@ -6,7 +6,7 @@ local eq, funcs = helpers.eq, helpers.funcs
if helpers.pending_win32(pending) then return end
-describe('Mouse input', function()
+describe('ui/mouse/input', function()
local screen
before_each(function()
diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua
index 54f43387dc..3f8173c8e2 100644
--- a/test/functional/ui/screen.lua
+++ b/test/functional/ui/screen.lua
@@ -313,6 +313,8 @@ function Screen:_redraw(updates)
if handler ~= nil then
handler(self, unpack(update[i]))
else
+ assert(self._on_event,
+ "Add Screen:_handle_XXX method or call Screen:set_on_event_handler")
self._on_event(method, update[i])
end
end
@@ -343,6 +345,10 @@ function Screen:_handle_resize(width, height)
}
end
+function Screen:_handle_cursor_style_set(style)
+ self._cursor_style = style
+end
+
function Screen:_handle_clear()
self:_clear_block(self._scroll_region.top, self._scroll_region.bot,
self._scroll_region.left, self._scroll_region.right)