aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/ci.yml1
-rw-r--r--.github/workflows/release.yml1
-rw-r--r--config/CMakeLists.txt1
-rw-r--r--config/config.h.in1
-rw-r--r--runtime/doc/sign.txt15
-rw-r--r--runtime/doc/syntax.txt4
-rw-r--r--runtime/filetype.vim3
-rw-r--r--src/nvim/CMakeLists.txt8
-rw-r--r--src/nvim/api/private/helpers.c2
-rw-r--r--src/nvim/globals.h2
-rw-r--r--src/nvim/highlight_defs.h4
-rw-r--r--src/nvim/message.c2
-rw-r--r--src/nvim/option.c1
-rw-r--r--src/nvim/os/os_defs.h4
-rw-r--r--src/nvim/os/pty_process_unix.c6
-rw-r--r--src/nvim/screen.c41
-rw-r--r--src/nvim/sign.c61
-rw-r--r--src/nvim/sign_defs.h1
-rw-r--r--src/nvim/strings.c12
-rw-r--r--src/nvim/syntax.c13
-rw-r--r--src/nvim/terminal.c7
-rw-r--r--src/nvim/testdir/test_filetype.vim3
-rw-r--r--src/nvim/testdir/test_highlight.vim7
-rw-r--r--src/nvim/testdir/test_signs.vim40
-rw-r--r--src/nvim/vim.h5
-rw-r--r--test/functional/terminal/buffer_spec.lua38
-rw-r--r--test/functional/ui/cursor_spec.lua4
-rw-r--r--test/functional/ui/fold_spec.lua112
-rw-r--r--test/functional/ui/sign_spec.lua93
-rw-r--r--test/helpers.lua14
30 files changed, 465 insertions, 41 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index d07b9fdac7..6a81ee4238 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -67,7 +67,6 @@ jobs:
# Workaround brew issues
rm -f /usr/local/bin/2to3
brew update >/dev/null
- brew upgrade
brew install automake ccache perl cpanminus ninja
- name: Setup interpreter packages
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index f1ed05e6cb..5839be2944 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -82,7 +82,6 @@ jobs:
run: |
rm -f /usr/local/bin/2to3
brew update >/dev/null
- brew upgrade
brew install automake ninja
- if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.tag_name != 'nightly')
run: printf 'NVIM_BUILD_TYPE=Release\n' >> $GITHUB_ENV
diff --git a/config/CMakeLists.txt b/config/CMakeLists.txt
index 613475b00d..30f08c5297 100644
--- a/config/CMakeLists.txt
+++ b/config/CMakeLists.txt
@@ -45,6 +45,7 @@ check_function_exists(readlink HAVE_READLINK)
check_function_exists(setpgid HAVE_SETPGID)
check_function_exists(setsid HAVE_SETSID)
check_function_exists(sigaction HAVE_SIGACTION)
+check_function_exists(strnlen HAVE_STRNLEN)
check_function_exists(strcasecmp HAVE_STRCASECMP)
check_function_exists(strncasecmp HAVE_STRNCASECMP)
check_function_exists(strptime HAVE_STRPTIME)
diff --git a/config/config.h.in b/config/config.h.in
index 27a28116af..b0635fb52b 100644
--- a/config/config.h.in
+++ b/config/config.h.in
@@ -30,6 +30,7 @@
#cmakedefine HAVE_SETPGID
#cmakedefine HAVE_SETSID
#cmakedefine HAVE_SIGACTION
+#cmakedefine HAVE_STRNLEN
#cmakedefine HAVE_STRCASECMP
#cmakedefine HAVE_STRINGS_H
#cmakedefine HAVE_STRNCASECMP
diff --git a/runtime/doc/sign.txt b/runtime/doc/sign.txt
index 895ee5ebef..68165f3d3d 100644
--- a/runtime/doc/sign.txt
+++ b/runtime/doc/sign.txt
@@ -47,6 +47,8 @@ The color of the column is set with the SignColumn highlight group
:highlight SignColumn guibg=darkgrey
<
+If 'cursorline' is enabled, then the CursorLineSign highlight group is used
+|hl-CursorLineSign|.
*sign-identifier*
Each placed sign is identified by a number called the sign identifier. This
identifier is used to jump to the sign or to remove the sign. The identifier
@@ -131,6 +133,10 @@ See |sign_define()| for the equivalent Vim script function.
texthl={group}
Highlighting group used for the text item.
+ culhl={group}
+ Highlighting group used for the text item when the cursor is
+ on the same line as the sign and 'cursorline' is enabled.
+
Example: >
:sign define MySign text=>> texthl=Search linehl=DiffText
<
@@ -377,6 +383,9 @@ sign_define({list})
text text that is displayed when there is no icon
or the GUI is not being used.
texthl highlight group used for the text item
+ culhl highlight group used for the text item when
+ the cursor is on the same line as the sign and
+ 'cursorline' is enabled.
numhl highlight group used for 'number' column at the
associated line. Overrides |hl-LineNr|,
|hl-CursorLineNr|.
@@ -425,9 +434,13 @@ sign_getdefined([{name}]) *sign_getdefined()*
or the GUI is not being used.
texthl highlight group used for the text item; not
present if not set.
+ culhl highlight group used for the text item when
+ the cursor is on the same line as the sign and
+ 'cursorline' is enabled; not present if not
+ set.
numhl highlight group used for 'number' column at the
associated line. Overrides |hl-LineNr|,
- |hl-CursorLineNr|.
+ |hl-CursorLineNr|; not present if not set.
Returns an empty List if there are no signs and when {name} is
not found.
diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt
index b57b8bfd5c..d49809599d 100644
--- a/runtime/doc/syntax.txt
+++ b/runtime/doc/syntax.txt
@@ -5127,6 +5127,10 @@ LineNrBelow Line number for when the 'relativenumber'
*hl-CursorLineNr*
CursorLineNr Like LineNr when 'cursorline' is set and 'cursorlineopt'
contains "number" or is "both", for the cursor line.
+ *hl-CursorLineSign*
+CursorLineSign Like SignColumn when 'cursorline' is set for the cursor line.
+ *hl-CursorLineFold*
+CursorLineFold Like FoldColumn when 'cursorline' is set for the cursor line.
*hl-MatchParen*
MatchParen The character under the cursor or just before it, if it
is a paired bracket, and its match. |pi_paren.txt|
diff --git a/runtime/filetype.vim b/runtime/filetype.vim
index e1c917ac64..3e57ae7f0f 100644
--- a/runtime/filetype.vim
+++ b/runtime/filetype.vim
@@ -1646,6 +1646,9 @@ au BufNewFile,BufRead .tcshrc,*.tcsh,tcsh.tcshrc,tcsh.login call dist#ft#SetFile
" (patterns ending in a start further below)
au BufNewFile,BufRead .login,.cshrc,csh.cshrc,csh.login,csh.logout,*.csh,.alias call dist#ft#CSH()
+" Zig
+au BufNewFile,BufRead *.zig setf zig
+
" Z-Shell script (patterns ending in a star further below)
au BufNewFile,BufRead .zprofile,*/etc/zprofile,.zfbfmarks setf zsh
au BufNewFile,BufRead .zshrc,.zshenv,.zlogin,.zlogout,.zcompdump setf zsh
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt
index 185d55daed..bb16459a7f 100644
--- a/src/nvim/CMakeLists.txt
+++ b/src/nvim/CMakeLists.txt
@@ -468,9 +468,11 @@ list(APPEND NVIM_LINK_LIBRARIES
if(UNIX)
list(APPEND NVIM_LINK_LIBRARIES
- m
- util
- )
+ m)
+ if (NOT CMAKE_SYSTEM_NAME STREQUAL "SunOS")
+ list(APPEND NVIM_LINK_LIBRARIES
+ util)
+ endif()
endif()
set(NVIM_EXEC_LINK_LIBRARIES ${NVIM_LINK_LIBRARIES} ${LUA_PREFERRED_LIBRARIES})
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index d470def277..9b407eab8b 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -513,7 +513,7 @@ String cbuf_to_string(const char *buf, size_t size)
String cstrn_to_string(const char *str, size_t maxsize)
FUNC_ATTR_NONNULL_ALL
{
- return cbuf_to_string(str, strnlen(str, maxsize));
+ return cbuf_to_string(str, STRNLEN(str, maxsize));
}
/// Creates a String using the given C string. Unlike
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index b2422fd531..938e9c7db7 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -997,6 +997,8 @@ EXTERN char e_non_empty_string_required[] INIT(= N_("E1142: Non-empty string req
EXTERN char e_cannot_define_autocommands_for_all_events[] INIT(= N_("E1155: Cannot define autocommands for ALL events"));
+EXTERN char e_highlight_group_name_too_long[] INIT(= N_("E1249: Highlight group name too long"));
+
EXTERN char top_bot_msg[] INIT(= N_("search hit TOP, continuing at BOTTOM"));
EXTERN char bot_top_msg[] INIT(= N_("search hit BOTTOM, continuing at TOP"));
diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h
index d4d53c4126..50a03e0c02 100644
--- a/src/nvim/highlight_defs.h
+++ b/src/nvim/highlight_defs.h
@@ -65,6 +65,8 @@ typedef enum {
HLF_LNA, // LineNrAbove
HLF_LNB, // LineNrBelow
HLF_CLN, // current line number when 'cursorline' is set
+ HLF_CLS, // current line sign column
+ HLF_CLF, // current line fold
HLF_R, // return to continue message and yes/no questions
HLF_S, // status lines
HLF_SNC, // status lines of not-current windows
@@ -122,6 +124,8 @@ EXTERN const char *hlf_names[] INIT(= {
[HLF_LNA] = "LineNrAbove",
[HLF_LNB] = "LineNrBelow",
[HLF_CLN] = "CursorLineNr",
+ [HLF_CLS] = "CursorLineSign",
+ [HLF_CLF] = "CursorLineFold",
[HLF_R] = "Question",
[HLF_S] = "StatusLine",
[HLF_SNC] = "StatusLineNC",
diff --git a/src/nvim/message.c b/src/nvim/message.c
index 8a6ac2decc..eaf7e2622a 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -2054,7 +2054,7 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr, int recurs
msg_ext_last_attr = attr;
}
// Concat pieces with the same highlight
- size_t len = strnlen((char *)str, maxlen); // -V781
+ size_t len = STRNLEN(str, maxlen); // -V781
ga_concat_len(&msg_ext_last_chunk, (char *)str, len);
msg_ext_cur_len += len;
return;
diff --git a/src/nvim/option.c b/src/nvim/option.c
index ecfdc85153..05929193b8 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -262,6 +262,7 @@ typedef struct vimoption {
#define HIGHLIGHT_INIT \
"8:SpecialKey,~:EndOfBuffer,z:TermCursor,Z:TermCursorNC,@:NonText,d:Directory,e:ErrorMsg," \
"i:IncSearch,l:Search,m:MoreMsg,M:ModeMsg,n:LineNr,a:LineNrAbove,b:LineNrBelow,N:CursorLineNr," \
+ "G:CursorLineSign,O:CursorLineFold" \
"r:Question,s:StatusLine,S:StatusLineNC,c:VertSplit,t:Title,v:Visual,V:VisualNOS,w:WarningMsg," \
"W:WildMenu,f:Folded,F:FoldColumn,A:DiffAdd,C:DiffChange,D:DiffDelete,T:DiffText,>:SignColumn," \
"-:Conceal,B:SpellBad,P:SpellCap,R:SpellRare,L:SpellLocal,+:Pmenu,=:PmenuSel,x:PmenuSbar," \
diff --git a/src/nvim/os/os_defs.h b/src/nvim/os/os_defs.h
index 8049b3b80e..dce4b0c187 100644
--- a/src/nvim/os/os_defs.h
+++ b/src/nvim/os/os_defs.h
@@ -13,6 +13,10 @@
# include "nvim/os/unix_defs.h"
#endif
+#if !defined(NAME_MAX) && defined(_XOPEN_NAME_MAX)
+#define NAME_MAX _XOPEN_NAME_MAX
+#endif
+
#define BASENAMELEN (NAME_MAX - 5)
// Use the system path length if it makes sense.
diff --git a/src/nvim/os/pty_process_unix.c b/src/nvim/os/pty_process_unix.c
index 24ecf5c24f..450bc75ffb 100644
--- a/src/nvim/os/pty_process_unix.c
+++ b/src/nvim/os/pty_process_unix.c
@@ -15,7 +15,7 @@
# include <libutil.h>
#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
# include <util.h>
-#else
+#elif !defined(__sun)
# include <pty.h>
#endif
@@ -198,7 +198,9 @@ static void init_termios(struct termios *termios) FUNC_ATTR_NONNULL_ALL
termios->c_cflag = CS8|CREAD;
termios->c_lflag = ISIG|ICANON|IEXTEN|ECHO|ECHOE|ECHOK;
- cfsetspeed(termios, 38400);
+ // not using cfsetspeed, not available on all platforms
+ cfsetispeed(termios, 38400);
+ cfsetospeed(termios, 38400);
#ifdef IUTF8
termios->c_iflag |= IUTF8;
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index d5b8b6db4c..0d06c45229 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -2759,7 +2759,11 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
p_extra = p_extra_free;
c_extra = NUL;
c_final = NUL;
- char_attr = win_hl_attr(wp, HLF_FC);
+ if (use_cursor_line_sign(wp, lnum)) {
+ char_attr = win_hl_attr(wp, HLF_CLF);
+ } else {
+ char_attr = win_hl_attr(wp, HLF_FC);
+ }
}
}
@@ -2770,7 +2774,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
* buffer or when using Netbeans. */
int count = win_signcol_count(wp);
if (count > 0) {
- get_sign_display_info(false, wp, sattrs, row,
+ get_sign_display_info(false, wp, lnum, sattrs, row,
startrow, filler_lines, filler_todo, count,
&c_extra, &c_final, extra, sizeof(extra),
&p_extra, &n_extra,
@@ -2791,7 +2795,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
if (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u'
&& num_signs > 0) {
int count = win_signcol_count(wp);
- get_sign_display_info(true, wp, sattrs, row,
+ get_sign_display_info(true, wp, lnum, sattrs, row,
startrow, filler_lines, filler_todo, count,
&c_extra, &c_final, extra, sizeof(extra),
&p_extra, &n_extra,
@@ -4630,6 +4634,14 @@ void screen_adjust_grid(ScreenGrid **grid, int *row_off, int *col_off)
}
}
+// Return true if CursorLineSign highlight is to be used.
+static bool use_cursor_line_sign(win_T *wp, linenr_T lnum)
+{
+ return wp->w_p_cul
+ && lnum == wp->w_cursor.lnum
+ && (wp->w_p_culopt_flags & CULOPT_NBR);
+}
+
// Get information needed to display the sign in line 'lnum' in window 'wp'.
// If 'nrcol' is TRUE, the sign is going to be displayed in the number column.
// Otherwise the sign is going to be displayed in the sign column.
@@ -4637,11 +4649,11 @@ void screen_adjust_grid(ScreenGrid **grid, int *row_off, int *col_off)
// @param count max number of signs
// @param[out] n_extrap number of characters from pp_extra to display
// @param[in, out] sign_idxp Index of the displayed sign
-static void get_sign_display_info(bool nrcol, win_T *wp, sign_attrs_T sattrs[], int row,
- int startrow, int filler_lines, int filler_todo, int count,
- int *c_extrap, int *c_finalp, char_u *extra, size_t extra_size,
- char_u **pp_extra, int *n_extrap, int *char_attrp,
- int *draw_statep, int *sign_idxp)
+static void get_sign_display_info(bool nrcol, win_T *wp, linenr_T lnum, sign_attrs_T sattrs[],
+ int row, int startrow, int filler_lines, int filler_todo,
+ int count, int *c_extrap, int *c_finalp, char_u *extra,
+ size_t extra_size, char_u **pp_extra, int *n_extrap,
+ int *char_attrp, int *draw_statep, int *sign_idxp)
{
// Draw cells with the sign value or blank.
*c_extrap = ' ';
@@ -4649,7 +4661,11 @@ static void get_sign_display_info(bool nrcol, win_T *wp, sign_attrs_T sattrs[],
if (nrcol) {
*n_extrap = number_width(wp) + 1;
} else {
- *char_attrp = win_hl_attr(wp, HLF_SC);
+ if (use_cursor_line_sign(wp, lnum)) {
+ *char_attrp = win_hl_attr(wp, HLF_CLS);
+ } else {
+ *char_attrp = win_hl_attr(wp, HLF_SC);
+ }
*n_extrap = win_signcol_width(wp);
}
@@ -4689,7 +4705,12 @@ static void get_sign_display_info(bool nrcol, win_T *wp, sign_attrs_T sattrs[],
(*pp_extra)[*n_extrap] = NUL;
}
}
- *char_attrp = sattr->sat_texthl;
+
+ if (use_cursor_line_sign(wp, lnum) && sattr->sat_culhl > 0) {
+ *char_attrp = sattr->sat_culhl;
+ } else {
+ *char_attrp = sattr->sat_texthl;
+ }
}
}
diff --git a/src/nvim/sign.c b/src/nvim/sign.c
index fca73dc9f2..32be714184 100644
--- a/src/nvim/sign.c
+++ b/src/nvim/sign.c
@@ -31,6 +31,7 @@ struct sign {
char_u *sn_text; // text used instead of pixmap
int sn_line_hl; // highlight ID for line
int sn_text_hl; // highlight ID for text
+ int sn_cul_hl; // highlight ID for text on current line when 'cursorline' is set
int sn_num_hl; // highlight ID for line number
};
@@ -499,6 +500,9 @@ int buf_get_signattrs(buf_T *buf, linenr_T lnum, sign_attrs_T sattrs[])
if (sp->sn_line_hl != 0) {
sattr.sat_linehl = syn_id2attr(sp->sn_line_hl);
}
+ if (sp->sn_cul_hl != 0) {
+ sattr.sat_culhl = syn_id2attr(sp->sn_cul_hl);
+ }
if (sp->sn_num_hl != 0) {
sattr.sat_numhl = syn_id2attr(sp->sn_num_hl);
}
@@ -901,7 +905,7 @@ static int sign_define_init_text(sign_T *sp, char_u *text)
/// Define a new sign or update an existing sign
int sign_define_by_name(char_u *name, char_u *icon, char_u *linehl, char_u *text, char_u *texthl,
- char *numhl)
+ char_u *culhl, char *numhl)
{
sign_T *sp_prev;
sign_T *sp;
@@ -939,15 +943,35 @@ int sign_define_by_name(char_u *name, char_u *icon, char_u *linehl, char_u *text
}
if (linehl != NULL) {
- sp->sn_line_hl = syn_check_group((char *)linehl, (int)STRLEN(linehl));
+ if (*linehl == NUL) {
+ sp->sn_line_hl = 0;
+ } else {
+ sp->sn_line_hl = syn_check_group((char *)linehl, (int)STRLEN(linehl));
+ }
}
if (texthl != NULL) {
- sp->sn_text_hl = syn_check_group((char *)texthl, (int)STRLEN(texthl));
+ if (*texthl == NUL) {
+ sp->sn_text_hl = 0;
+ } else {
+ sp->sn_text_hl = syn_check_group((char *)texthl, (int)STRLEN(texthl));
+ }
+ }
+
+ if (culhl != NULL) {
+ if (*culhl == NUL) {
+ sp->sn_cul_hl = 0;
+ } else {
+ sp->sn_cul_hl = syn_check_group((char *)culhl, (int)STRLEN(culhl));
+ }
}
if (numhl != NULL) {
- sp->sn_num_hl = syn_check_group(numhl, (int)STRLEN(numhl));
+ if (*numhl == NUL) {
+ sp->sn_num_hl = 0;
+ } else {
+ sp->sn_num_hl = syn_check_group(numhl, (int)STRLEN(numhl));
+ }
}
return OK;
@@ -1133,6 +1157,7 @@ static void sign_define_cmd(char_u *sign_name, char_u *cmdline)
char_u *text = NULL;
char_u *linehl = NULL;
char_u *texthl = NULL;
+ char_u *culhl = NULL;
char_u *numhl = NULL;
int failed = false;
@@ -1155,6 +1180,9 @@ static void sign_define_cmd(char_u *sign_name, char_u *cmdline)
} else if (STRNCMP(arg, "texthl=", 7) == 0) {
arg += 7;
texthl = vim_strnsave(arg, (size_t)(p - arg));
+ } else if (STRNCMP(arg, "culhl=", 6) == 0) {
+ arg += 6;
+ culhl = vim_strnsave(arg, (size_t)(p - arg));
} else if (STRNCMP(arg, "numhl=", 6) == 0) {
arg += 6;
numhl = vim_strnsave(arg, (size_t)(p - arg));
@@ -1166,13 +1194,14 @@ static void sign_define_cmd(char_u *sign_name, char_u *cmdline)
}
if (!failed) {
- sign_define_by_name(sign_name, icon, linehl, text, texthl, (char *)numhl);
+ sign_define_by_name(sign_name, icon, linehl, text, texthl, culhl, (char *)numhl);
}
xfree(icon);
xfree(text);
xfree(linehl);
xfree(texthl);
+ xfree(culhl);
xfree(numhl);
}
@@ -1481,6 +1510,13 @@ static void sign_getinfo(sign_T *sp, dict_T *retdict)
}
tv_dict_add_str(retdict, S_LEN("texthl"), (char *)p);
}
+ if (sp->sn_cul_hl > 0) {
+ p = get_highlight_name_ext(NULL, sp->sn_cul_hl - 1, false);
+ if (p == NULL) {
+ p = "NONE";
+ }
+ tv_dict_add_str(retdict, S_LEN("culhl"), (char *)p);
+ }
if (sp->sn_num_hl > 0) {
p = get_highlight_name_ext(NULL, sp->sn_num_hl - 1, false);
if (p == NULL) {
@@ -1609,6 +1645,16 @@ static void sign_list_defined(sign_T *sp)
msg_puts(p);
}
}
+ if (sp->sn_cul_hl > 0) {
+ msg_puts(" culhl=");
+ const char *const p = get_highlight_name_ext(NULL,
+ sp->sn_cul_hl - 1, false);
+ if (p == NULL) {
+ msg_puts("NONE");
+ } else {
+ msg_puts(p);
+ }
+ }
if (sp->sn_num_hl > 0) {
msg_puts(" numhl=");
const char *const p = get_highlight_name_ext(NULL,
@@ -1847,6 +1893,7 @@ int sign_define_from_dict(const char *name_arg, dict_T *dict)
char *linehl = NULL;
char *text = NULL;
char *texthl = NULL;
+ char *culhl = NULL;
char *numhl = NULL;
int retval = -1;
@@ -1866,11 +1913,12 @@ int sign_define_from_dict(const char *name_arg, dict_T *dict)
linehl = tv_dict_get_string(dict, "linehl", true);
text = tv_dict_get_string(dict, "text", true);
texthl = tv_dict_get_string(dict, "texthl", true);
+ culhl = tv_dict_get_string(dict, "culhl", true);
numhl = tv_dict_get_string(dict, "numhl", true);
}
if (sign_define_by_name((char_u *)name, (char_u *)icon, (char_u *)linehl,
- (char_u *)text, (char_u *)texthl, numhl)
+ (char_u *)text, (char_u *)texthl, (char_u *)culhl, numhl)
== OK) {
retval = 0;
}
@@ -1881,6 +1929,7 @@ cleanup:
xfree(linehl);
xfree(text);
xfree(texthl);
+ xfree(culhl);
xfree(numhl);
return retval;
diff --git a/src/nvim/sign_defs.h b/src/nvim/sign_defs.h
index 46436b2c8e..c734502878 100644
--- a/src/nvim/sign_defs.h
+++ b/src/nvim/sign_defs.h
@@ -38,6 +38,7 @@ typedef struct sign_attrs_S {
char_u *sat_text;
int sat_texthl;
int sat_linehl;
+ int sat_culhl;
int sat_numhl;
} sign_attrs_T;
diff --git a/src/nvim/strings.c b/src/nvim/strings.c
index c58e052ae9..27f93fe4ce 100644
--- a/src/nvim/strings.c
+++ b/src/nvim/strings.c
@@ -394,6 +394,18 @@ void del_trailing_spaces(char_u *ptr)
}
}
+#if !defined(HAVE_STRNLEN)
+size_t xstrnlen(const char *s, size_t n)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE
+{
+ const char *end = memchr(s, '\0', n);
+ if (end == NULL) {
+ return n;
+ }
+ return end - s;
+}
+#endif
+
#if (!defined(HAVE_STRCASECMP) && !defined(HAVE_STRICMP))
/*
* Compare two strings, ignoring case, using current locale.
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index fc445d80b6..a39f78b751 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -116,6 +116,8 @@ static int include_none = 0; // when 1 include "nvim/None"
static int include_default = 0; // when 1 include "nvim/default"
static int include_link = 0; // when 2 include "nvim/link" and "clear"
+#define MAX_SYN_NAME 200
+
/// The "term", "cterm" and "gui" arguments can be any combination of the
/// following names, separated by commas (but no spaces!).
static char *(hl_name_table[]) =
@@ -6176,6 +6178,8 @@ static const char *highlight_init_both[] = {
"default link LineNrAbove LineNr",
"default link LineNrBelow LineNr",
"default link QuickFixLine Search",
+ "default link CursorLineSign SignColumn",
+ "default link CursorLineFold FoldColumn",
"default link Substitute Search",
"default link Whitespace NonText",
"default link MsgSeparator StatusLine",
@@ -7624,10 +7628,9 @@ int syn_name2id(const char *name)
int syn_name2id_len(const char_u *name, size_t len)
FUNC_ATTR_NONNULL_ALL
{
- char name_u[201];
+ char name_u[MAX_SYN_NAME + 1];
- if (len == 0 || len > 200) {
- // ID names over 200 chars don't deserve to be found!
+ if (len == 0 || len > MAX_SYN_NAME) {
return 0;
}
@@ -7685,6 +7688,10 @@ char_u *syn_id2name(int id)
/// @return 0 for failure else the id of the group
int syn_check_group(const char *name, int len)
{
+ if (len > MAX_SYN_NAME) {
+ emsg(_(e_highlight_group_name_too_long));
+ return 0;
+ }
int id = syn_name2id_len((char_u *)name, len);
if (id == 0) { // doesn't exist yet
return syn_add_group(vim_strnsave((char_u *)name, len));
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c
index afebda4948..2d32b6bac4 100644
--- a/src/nvim/terminal.c
+++ b/src/nvim/terminal.c
@@ -1507,6 +1507,13 @@ static void refresh_screen(Terminal *term, buf_T *buf)
// Terminal height may have decreased before `invalid_end` reflects it.
term->invalid_end = MIN(term->invalid_end, height);
+ // There are no invalid rows.
+ if (term->invalid_start >= term->invalid_end) {
+ term->invalid_start = INT_MAX;
+ term->invalid_end = -1;
+ return;
+ }
+
for (int r = term->invalid_start, linenr = row_to_linenr(term, r);
r < term->invalid_end; r++, linenr++) {
fetch_row(term, r, width);
diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim
index 69edbc227d..31052ce47d 100644
--- a/src/nvim/testdir/test_filetype.vim
+++ b/src/nvim/testdir/test_filetype.vim
@@ -502,7 +502,7 @@ let s:filename_checks = {
\ 'tex': ['file.latex', 'file.sty', 'file.dtx', 'file.ltx', 'file.bbl'],
\ 'texinfo': ['file.texinfo', 'file.texi', 'file.txi'],
\ 'texmf': ['texmf.cnf'],
- \ 'text': ['file.text', 'README', 'LICENSE', 'COPYING', 'AUTHORS', '/usr/share/doc/bash-completion/AUTHORS', '/etc/apt/apt.conf.d/README', '/etc/Muttrc.d/README'],
+ \ 'text': ['file.text', 'file.txt', 'README', 'LICENSE', 'COPYING', 'AUTHORS', '/usr/share/doc/bash-completion/AUTHORS', '/etc/apt/apt.conf.d/README', '/etc/Muttrc.d/README'],
\ 'tf': ['file.tf', '.tfrc', 'tfrc'],
\ 'tidy': ['.tidyrc', 'tidyrc', 'tidy.conf'],
\ 'tilde': ['file.t.html'],
@@ -568,6 +568,7 @@ let s:filename_checks = {
\ 'yaml': ['file.yaml', 'file.yml'],
\ 'raml': ['file.raml'],
\ 'z8a': ['file.z8a'],
+ \ 'zig': ['file.zig'],
\ 'zimbu': ['file.zu'],
\ 'zimbutempl': ['file.zut'],
\ 'zsh': ['.zprofile', '/etc/zprofile', '.zfbfmarks', 'file.zsh', '.zcompdump', '.zlogin', '.zlogout', '.zshenv', '.zshrc', '.zcompdump-file', '.zlog', '.zlog-file', '.zsh', '.zsh-file', 'any/etc/zprofile', 'zlog', 'zlog-file', 'zsh', 'zsh-file'],
diff --git a/src/nvim/testdir/test_highlight.vim b/src/nvim/testdir/test_highlight.vim
index c38bfa5677..899eb530ec 100644
--- a/src/nvim/testdir/test_highlight.vim
+++ b/src/nvim/testdir/test_highlight.vim
@@ -661,6 +661,13 @@ function Test_no_space_before_xxx()
let &columns = l:org_columns
endfunction
+" Test for :highlight command errors
+func Test_highlight_cmd_errors()
+ if has('gui_running') || has('nvim')
+ call assert_fails('hi ' .. repeat('a', 201) .. ' ctermfg=black', 'E1249:')
+ endif
+endfunc
+
" Test for using RGB color values in a highlight group
func Test_xxlast_highlight_RGB_color()
CheckCanRunGui
diff --git a/src/nvim/testdir/test_signs.vim b/src/nvim/testdir/test_signs.vim
index f287256396..799e6cb57b 100644
--- a/src/nvim/testdir/test_signs.vim
+++ b/src/nvim/testdir/test_signs.vim
@@ -15,13 +15,13 @@ func Test_sign()
" the icon name when listing signs.
sign define Sign1 text=x
- call Sign_command_ignore_error('sign define Sign2 text=xy texthl=Title linehl=Error icon=../../pixmaps/stock_vim_find_help.png')
+ call Sign_command_ignore_error('sign define Sign2 text=xy texthl=Title linehl=Error culhl=Search icon=../../pixmaps/stock_vim_find_help.png')
" Test listing signs.
let a=execute('sign list')
call assert_match('^\nsign Sign1 text=x \nsign Sign2 ' .
\ 'icon=../../pixmaps/stock_vim_find_help.png .*text=xy ' .
- \ 'linehl=Error texthl=Title$', a)
+ \ 'linehl=Error texthl=Title culhl=Search$', a)
let a=execute('sign list Sign1')
call assert_equal("\nsign Sign1 text=x ", a)
@@ -126,6 +126,30 @@ func Test_sign()
" call assert_fails("sign define Sign4 text= linehl=Comment", 'E239:')
call assert_fails("sign define Sign4 text=\\ ab linehl=Comment", 'E239:')
+ " an empty highlight argument for an existing sign clears it
+ sign define SignY texthl=TextHl culhl=CulHl linehl=LineHl
+ let sl = sign_getdefined('SignY')[0]
+ call assert_equal('TextHl', sl.texthl)
+ call assert_equal('CulHl', sl.culhl)
+ call assert_equal('LineHl', sl.linehl)
+
+ sign define SignY texthl= culhl=CulHl linehl=LineHl
+ let sl = sign_getdefined('SignY')[0]
+ call assert_false(has_key(sl, 'texthl'))
+ call assert_equal('CulHl', sl.culhl)
+ call assert_equal('LineHl', sl.linehl)
+
+ sign define SignY linehl=
+ let sl = sign_getdefined('SignY')[0]
+ call assert_false(has_key(sl, 'linehl'))
+ call assert_equal('CulHl', sl.culhl)
+
+ sign define SignY culhl=
+ let sl = sign_getdefined('SignY')[0]
+ call assert_false(has_key(sl, 'culhl'))
+
+ sign undefine SignY
+
" define sign with whitespace
sign define Sign4 text=\ X linehl=Comment
sign undefine Sign4
@@ -392,19 +416,21 @@ func Test_sign_funcs()
call sign_undefine()
" Tests for sign_define()
- let attr = {'text' : '=>', 'linehl' : 'Search', 'texthl' : 'Error'}
+ let attr = {'text' : '=>', 'linehl' : 'Search', 'texthl' : 'Error',
+ \ 'culhl': 'Visual'}
call assert_equal(0, "sign1"->sign_define(attr))
call assert_equal([{'name' : 'sign1', 'texthl' : 'Error',
- \ 'linehl' : 'Search', 'text' : '=>'}], sign_getdefined())
+ \ 'linehl' : 'Search', 'culhl': 'Visual', 'text' : '=>'}],
+ \ sign_getdefined())
" Define a new sign without attributes and then update it
call sign_define("sign2")
let attr = {'text' : '!!', 'linehl' : 'DiffAdd', 'texthl' : 'DiffChange',
- \ 'icon' : 'sign2.ico'}
+ \ 'culhl': 'DiffDelete', 'icon' : 'sign2.ico'}
call Sign_define_ignore_error("sign2", attr)
call assert_equal([{'name' : 'sign2', 'texthl' : 'DiffChange',
- \ 'linehl' : 'DiffAdd', 'text' : '!!', 'icon' : 'sign2.ico'}],
- \ "sign2"->sign_getdefined())
+ \ 'linehl' : 'DiffAdd', 'culhl' : 'DiffDelete', 'text' : '!!',
+ \ 'icon' : 'sign2.ico'}], "sign2"->sign_getdefined())
" Test for a sign name with digits
call assert_equal(0, sign_define(0002, {'linehl' : 'StatusLine'}))
diff --git a/src/nvim/vim.h b/src/nvim/vim.h
index e3539c1a57..2f8ddd1e88 100644
--- a/src/nvim/vim.h
+++ b/src/nvim/vim.h
@@ -215,6 +215,11 @@ enum { FOLD_TEXT_LEN = 51, }; //!< buffer size for get_foldtext()
// (vim_strchr() is now in strings.c)
#define STRLEN(s) strlen((char *)(s))
+#ifdef HAVE_STRNLEN
+# define STRNLEN(s, n) strnlen((char *)(s), (size_t)(n))
+#else
+# define STRNLEN(s, n) xstrnlen((char *)(s), (size_t)(n))
+#endif
#define STRCPY(d, s) strcpy((char *)(d), (char *)(s))
#define STRNCPY(d, s, n) strncpy((char *)(d), (char *)(s), (size_t)(n))
#define STRLCPY(d, s, n) xstrlcpy((char *)(d), (char *)(s), (size_t)(n))
diff --git a/test/functional/terminal/buffer_spec.lua b/test/functional/terminal/buffer_spec.lua
index 7dcca231ee..f25cfa2039 100644
--- a/test/functional/terminal/buffer_spec.lua
+++ b/test/functional/terminal/buffer_spec.lua
@@ -6,9 +6,11 @@ local poke_eventloop = helpers.poke_eventloop
local eval, feed_command, source = helpers.eval, helpers.feed_command, helpers.source
local eq, neq = helpers.eq, helpers.neq
local write_file = helpers.write_file
-local command= helpers.command
+local command = helpers.command
local exc_exec = helpers.exc_exec
local matches = helpers.matches
+local exec_lua = helpers.exec_lua
+local sleep = helpers.sleep
describe(':terminal buffer', function()
local screen
@@ -328,3 +330,37 @@ describe('No heap-buffer-overflow when', function()
assert_alive()
end)
end)
+
+describe('on_lines does not emit out-of-bounds line indexes when', function()
+ before_each(function()
+ clear()
+ exec_lua([[
+ function _G.register_callback(bufnr)
+ _G.cb_error = ''
+ vim.api.nvim_buf_attach(bufnr, false, {
+ on_lines = function(_, bufnr, _, firstline, _, _)
+ local status, msg = pcall(vim.api.nvim_buf_get_offset, bufnr, firstline)
+ if not status then
+ _G.cb_error = msg
+ end
+ end
+ })
+ end
+ ]])
+ end)
+
+ it('creating a terminal buffer #16394', function()
+ feed_command([[autocmd TermOpen * ++once call v:lua.register_callback(expand("<abuf>"))]])
+ feed_command('terminal')
+ sleep(500)
+ eq('', exec_lua([[return _G.cb_error]]))
+ end)
+
+ it('deleting a terminal buffer #16394', function()
+ feed_command('terminal')
+ sleep(500)
+ feed_command('lua _G.register_callback(0)')
+ feed_command('bdelete!')
+ eq('', exec_lua([[return _G.cb_error]]))
+ end)
+end)
diff --git a/test/functional/ui/cursor_spec.lua b/test/functional/ui/cursor_spec.lua
index 9c035c728b..03cd4bfd06 100644
--- a/test/functional/ui/cursor_spec.lua
+++ b/test/functional/ui/cursor_spec.lua
@@ -212,10 +212,10 @@ describe('ui/cursor', function()
if m.blinkwait then m.blinkwait = 700 end
end
if m.hl_id then
- m.hl_id = 58
+ m.hl_id = 60
m.attr = {background = Screen.colors.DarkGray}
end
- if m.id_lm then m.id_lm = 59 end
+ if m.id_lm then m.id_lm = 61 end
end
-- Assert the new expectation.
diff --git a/test/functional/ui/fold_spec.lua b/test/functional/ui/fold_spec.lua
index 249686234c..3e0e15c2b7 100644
--- a/test/functional/ui/fold_spec.lua
+++ b/test/functional/ui/fold_spec.lua
@@ -41,6 +41,7 @@ describe("folded lines", function()
[9] = {bold = true, foreground = Screen.colors.Brown},
[10] = {background = Screen.colors.LightGrey, underline = true},
[11] = {bold=true},
+ [12] = {background = Screen.colors.Grey90},
})
end)
@@ -84,6 +85,117 @@ describe("folded lines", function()
end
end)
+ it("highlights with CursorLineFold when 'cursorline' is set", function()
+ command("set cursorline foldcolumn=2 foldmethod=marker")
+ command("hi link CursorLineFold Search")
+ insert(content1)
+ feed("zf3j")
+ if multigrid then
+ screen:expect([[
+ ## grid 1
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [3:---------------------------------------------]|
+ ## grid 2
+ {7: }This is a |
+ {7: }valid English |
+ {7: }sentence composed by |
+ {7: }an exhausted developer |
+ {7: }in his cave. |
+ {6: }{12:^ }|
+ {1:~ }|
+ ## grid 3
+ |
+ ]])
+ else
+ screen:expect([[
+ {7: }This is a |
+ {7: }valid English |
+ {7: }sentence composed by |
+ {7: }an exhausted developer |
+ {7: }in his cave. |
+ {6: }{12:^ }|
+ {1:~ }|
+ |
+ ]])
+ end
+ feed("k")
+ if multigrid then
+ screen:expect([[
+ ## grid 1
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [3:---------------------------------------------]|
+ ## grid 2
+ {7: }This is a |
+ {7: }valid English |
+ {7: }sentence composed by |
+ {7: }an exhausted developer |
+ {6: }{12:^in his cave. }|
+ {7: } |
+ {1:~ }|
+ ## grid 3
+ |
+ ]])
+ else
+ screen:expect([[
+ {7: }This is a |
+ {7: }valid English |
+ {7: }sentence composed by |
+ {7: }an exhausted developer |
+ {6: }{12:^in his cave. }|
+ {7: } |
+ {1:~ }|
+ |
+ ]])
+ end
+ command("set cursorlineopt=line")
+ if multigrid then
+ screen:expect([[
+ ## grid 1
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [3:---------------------------------------------]|
+ ## grid 2
+ {7: }This is a |
+ {7: }valid English |
+ {7: }sentence composed by |
+ {7: }an exhausted developer |
+ {7: }{12:^in his cave. }|
+ {7: } |
+ {1:~ }|
+ ## grid 3
+ |
+ ]])
+ else
+ screen:expect([[
+ {7: }This is a |
+ {7: }valid English |
+ {7: }sentence composed by |
+ {7: }an exhausted developer |
+ {7: }{12:^in his cave. }|
+ {7: } |
+ {1:~ }|
+ |
+ ]])
+ end
+ end)
+
it("highlighting with relative line numbers", function()
command("set relativenumber cursorline cursorlineopt=number foldmethod=marker")
feed_command("set foldcolumn=2")
diff --git a/test/functional/ui/sign_spec.lua b/test/functional/ui/sign_spec.lua
index 741b93043d..dcd31cfdb7 100644
--- a/test/functional/ui/sign_spec.lua
+++ b/test/functional/ui/sign_spec.lua
@@ -157,6 +157,99 @@ describe('Signs', function()
]])
end)
+ it('higlights the cursorline sign with culhl', function()
+ feed('ia<cr>b<cr>c<esc>')
+ command('sign define piet text=>> texthl=Search culhl=ErrorMsg')
+ command('sign place 1 line=1 name=piet buffer=1')
+ command('sign place 2 line=2 name=piet buffer=1')
+ command('sign place 3 line=3 name=piet buffer=1')
+ command('set cursorline')
+ screen:expect([[
+ {1:>>}a |
+ {1:>>}b |
+ {8:>>}{3:^c }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ feed('k')
+ screen:expect([[
+ {1:>>}a |
+ {8:>>}{3:^b }|
+ {1:>>}c |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ command('set nocursorline')
+ screen:expect([[
+ {1:>>}a |
+ {1:>>}^b |
+ {1:>>}c |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ command('set cursorline cursorlineopt=line')
+ screen:expect([[
+ {1:>>}a |
+ {1:>>}{3:^b }|
+ {1:>>}c |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ command('set cursorlineopt=number')
+ screen:expect([[
+ {1:>>}a |
+ {8:>>}^b |
+ {1:>>}c |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ end)
+
it('multiple signs #9295', function()
feed('ia<cr>b<cr>c<cr><esc>')
command('set number')
diff --git a/test/helpers.lua b/test/helpers.lua
index 09b113c01d..87431e4342 100644
--- a/test/helpers.lua
+++ b/test/helpers.lua
@@ -741,9 +741,20 @@ function module.read_file_list(filename, start)
if not file then
return nil
end
+
+ -- There is no need to read more than the last 2MB of the log file, so seek
+ -- to that.
+ local file_size = file:seek("end")
+ local offset = file_size - 2000000
+ if offset < 0 then
+ offset = 0
+ end
+ file:seek("set", offset)
+
local lines = {}
local i = 1
- for line in file:lines() do
+ local line = file:read("*l")
+ while line ~= nil do
if i >= start then
table.insert(lines, line)
if #lines > maxlines then
@@ -751,6 +762,7 @@ function module.read_file_list(filename, start)
end
end
i = i + 1
+ line = file:read("*l")
end
file:close()
return lines