aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/api/buffer.c10
-rw-r--r--src/nvim/buffer.c1
-rw-r--r--src/nvim/buffer_defs.h1
-rw-r--r--src/nvim/change.c16
-rw-r--r--src/nvim/eval.c23
-rw-r--r--src/nvim/eval/funcs.c9
-rw-r--r--src/nvim/ex_getln.c34
-rw-r--r--src/nvim/extmark.c14
-rw-r--r--src/nvim/fileio.c3
-rw-r--r--src/nvim/fold.c44
-rw-r--r--src/nvim/fold.h1
-rw-r--r--src/nvim/log.h5
-rw-r--r--src/nvim/macros.h6
-rw-r--r--src/nvim/ops.c29
-rw-r--r--src/nvim/option.c8
-rw-r--r--src/nvim/option_defs.h1
-rw-r--r--src/nvim/options.lua10
-rw-r--r--src/nvim/screen.c440
-rw-r--r--src/nvim/spell.c24
-rw-r--r--src/nvim/syntax.c2
-rw-r--r--src/nvim/testdir/check.vim8
-rw-r--r--src/nvim/testdir/test_autocmd.vim31
-rw-r--r--src/nvim/testdir/test_edit.vim34
-rw-r--r--src/nvim/testdir/test_filetype.vim4
-rw-r--r--src/nvim/testdir/test_functions.vim37
-rw-r--r--src/nvim/testdir/test_gf.vim27
-rw-r--r--src/nvim/testdir/test_search.vim18
-rw-r--r--src/nvim/testdir/test_spell.vim5
-rw-r--r--src/nvim/testdir/test_syntax.vim4
-rw-r--r--src/nvim/testdir/test_timers.vim7
-rw-r--r--src/nvim/window.c6
31 files changed, 439 insertions, 423 deletions
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index 6f74ec905c..e77870dcf3 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -1393,15 +1393,17 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id,
}
if (col2 >= 0) {
- if (line2 >= 0) {
- len = STRLEN(ml_get_buf(buf, (linenr_T)line2+1, false));
+ if (line2 >= 0 && line2 < buf->b_ml.ml_line_count) {
+ len = STRLEN(ml_get_buf(buf, (linenr_T)line2 + 1, false));
+ } else if (line2 == buf->b_ml.ml_line_count) {
+ // We are trying to add an extmark past final newline
+ len = 0;
} else {
// reuse len from before
line2 = (int)line;
}
if (col2 > (Integer)len) {
- api_set_error(err, kErrorTypeValidation,
- "end_col value outside range");
+ api_set_error(err, kErrorTypeValidation, "end_col value outside range");
goto error;
}
} else if (line2 >= 0) {
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 4648631ebe..6f1d9b2365 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -1941,6 +1941,7 @@ void free_buf_options(buf_T *buf, int free_p_ff)
vim_regfree(buf->b_s.b_cap_prog);
buf->b_s.b_cap_prog = NULL;
clear_string_option(&buf->b_s.b_p_spl);
+ clear_string_option(&buf->b_s.b_p_spo);
clear_string_option(&buf->b_p_sua);
clear_string_option(&buf->b_p_ft);
clear_string_option(&buf->b_p_cink);
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index bd9cd2f5ec..ea968d9592 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -451,6 +451,7 @@ typedef struct {
regprog_T *b_cap_prog; // program for 'spellcapcheck'
char_u *b_p_spf; // 'spellfile'
char_u *b_p_spl; // 'spelllang'
+ char_u *b_p_spo; // 'spelloptions'
int b_cjk; // all CJK letters as OK
char_u b_syn_chartab[32]; // syntax iskeyword option
char_u *b_syn_isk; // iskeyword option
diff --git a/src/nvim/change.c b/src/nvim/change.c
index b8bc08b747..71614363d2 100644
--- a/src/nvim/change.c
+++ b/src/nvim/change.c
@@ -362,8 +362,7 @@ void changed_bytes(linenr_T lnum, colnr_T col)
/// insert/delete bytes at column
///
/// Like changed_bytes() but also adjust extmark for "new" bytes.
-/// When "new" is negative text was deleted.
-static void inserted_bytes(linenr_T lnum, colnr_T col, int old, int new)
+void inserted_bytes(linenr_T lnum, colnr_T col, int old, int new)
{
if (curbuf_splice_pending == 0) {
extmark_splice_cols(curbuf, (int)lnum-1, col, old, new, kExtmarkUndo);
@@ -1677,9 +1676,16 @@ int open_line(
truncate_spaces(saved_line);
}
ml_replace(curwin->w_cursor.lnum, saved_line, false);
- extmark_splice_cols(
- curbuf, (int)curwin->w_cursor.lnum,
- 0, curwin->w_cursor.col, (int)STRLEN(saved_line), kExtmarkUndo);
+
+ int new_len = (int)STRLEN(saved_line);
+
+ // TODO(vigoux): maybe there is issues there with expandtabs ?
+ if (new_len < curwin->w_cursor.col) {
+ extmark_splice_cols(
+ curbuf, (int)curwin->w_cursor.lnum,
+ new_len, curwin->w_cursor.col - new_len, 0, kExtmarkUndo);
+ }
+
saved_line = NULL;
if (did_append) {
changed_lines(curwin->w_cursor.lnum, curwin->w_cursor.col,
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 32830c5d7f..00542e3766 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -4518,7 +4518,6 @@ int get_option_tv(const char **const arg, typval_T *const rettv,
static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate)
{
char_u *p;
- char_u *name;
unsigned int extra = 0;
/*
@@ -4526,11 +4525,14 @@ static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate)
*/
for (p = *arg + 1; *p != NUL && *p != '"'; MB_PTR_ADV(p)) {
if (*p == '\\' && p[1] != NUL) {
- ++p;
- /* A "\<x>" form occupies at least 4 characters, and produces up
- * to 6 characters: reserve space for 2 extra */
- if (*p == '<')
- extra += 2;
+ p++;
+ // A "\<x>" form occupies at least 4 characters, and produces up
+ // to 21 characters (3 * 6 for the char and 3 for a modifier):
+ // reserve space for 18 extra.
+ // Each byte in the char could be encoded as K_SPECIAL K_EXTRA x.
+ if (*p == '<') {
+ extra += 18;
+ }
}
}
@@ -4549,7 +4551,8 @@ static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate)
* Copy the string into allocated memory, handling backslashed
* characters.
*/
- name = xmalloc(p - *arg + extra);
+ const int len = (int)(p - *arg + extra);
+ char_u *name = xmalloc(len);
rettv->v_type = VAR_STRING;
rettv->vval.v_string = name;
@@ -4616,6 +4619,9 @@ static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate)
extra = trans_special((const char_u **)&p, STRLEN(p), name, true, true);
if (extra != 0) {
name += extra;
+ if (name >= rettv->vval.v_string + len) {
+ iemsg("get_string_tv() used more space than allocated");
+ }
break;
}
FALLTHROUGH;
@@ -6962,9 +6968,10 @@ void set_buffer_lines(buf_T *buf, linenr_T lnum_arg, bool append,
if (!append && lnum <= curbuf->b_ml.ml_line_count) {
// Existing line, replace it.
+ int old_len = (int)STRLEN(ml_get(lnum));
if (u_savesub(lnum) == OK
&& ml_replace(lnum, (char_u *)line, true) == OK) {
- changed_bytes(lnum, 0);
+ inserted_bytes(lnum, 0, old_len, STRLEN(line));
if (is_curbuf && lnum == curwin->w_cursor.lnum) {
check_cursor_col();
}
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 3a4b4f2a50..bd77a3b7e2 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -2584,8 +2584,6 @@ static void f_foldtextresult(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
char_u *text;
char_u buf[FOLD_TEXT_LEN];
- foldinfo_T foldinfo;
- int fold_count;
static bool entered = false;
rettv->v_type = VAR_STRING;
@@ -2599,9 +2597,10 @@ static void f_foldtextresult(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (lnum < 0) {
lnum = 0;
}
- fold_count = foldedCount(curwin, lnum, &foldinfo);
- if (fold_count > 0) {
- text = get_foldtext(curwin, lnum, lnum + fold_count - 1, &foldinfo, buf);
+
+ foldinfo_T info = fold_info(curwin, lnum);
+ if (info.fi_lines > 0) {
+ text = get_foldtext(curwin, lnum, lnum + info.fi_lines - 1, info, buf);
if (text == buf) {
text = vim_strsave(text);
}
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index 8f2d536e63..f9ca7bfa42 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -288,6 +288,7 @@ static bool do_incsearch_highlighting(int firstc, incsearch_state_T *s,
exarg_T ea;
pos_T save_cursor;
bool use_last_pat;
+ bool retval = false;
*skiplen = 0;
*patlen = ccline.cmdlen;
@@ -307,6 +308,7 @@ static bool do_incsearch_highlighting(int firstc, incsearch_state_T *s,
return false;
}
+ emsg_off++;
memset(&ea, 0, sizeof(ea));
ea.line1 = 1;
ea.line2 = 1;
@@ -318,13 +320,13 @@ static bool do_incsearch_highlighting(int firstc, incsearch_state_T *s,
cmd = skip_range(ea.cmd, NULL);
if (vim_strchr((char_u *)"sgvl", *cmd) == NULL) {
- return false;
+ goto theend;
}
// Skip over "substitute" to find the pattern separator.
for (p = cmd; ASCII_ISALPHA(*p); p++) {}
if (*skipwhite(p) == NUL) {
- return false;
+ goto theend;
}
if (STRNCMP(cmd, "substitute", p - cmd) == 0
@@ -337,12 +339,15 @@ static bool do_incsearch_highlighting(int firstc, incsearch_state_T *s,
p_magic = false;
}
} else if (STRNCMP(cmd, "sort", MAX(p - cmd, 3)) == 0) {
- // skip over flags.
+ // skip over ! and flags
+ if (*p == '!') {
+ p = skipwhite(p + 1);
+ }
while (ASCII_ISALPHA(*(p = skipwhite(p)))) {
p++;
}
if (*p == NUL) {
- return false;
+ goto theend;
}
} else if (STRNCMP(cmd, "vimgrep", MAX(p - cmd, 3)) == 0
|| STRNCMP(cmd, "vimgrepadd", MAX(p - cmd, 8)) == 0
@@ -353,14 +358,14 @@ static bool do_incsearch_highlighting(int firstc, incsearch_state_T *s,
if (*p == '!') {
p++;
if (*skipwhite(p) == NUL) {
- return false;
+ goto theend;
}
}
if (*cmd != 'g') {
delim_optional = true;
}
} else {
- return false;
+ goto theend;
}
p = skipwhite(p);
@@ -369,7 +374,7 @@ static bool do_incsearch_highlighting(int firstc, incsearch_state_T *s,
use_last_pat = end == p && *end == delim;
if (end == p && !use_last_pat) {
- return false;
+ goto theend;
}
// Don't do 'hlsearch' highlighting if the pattern matches everything.
@@ -381,7 +386,7 @@ static bool do_incsearch_highlighting(int firstc, incsearch_state_T *s,
empty = empty_pattern(p);
*end = c;
if (empty) {
- return false;
+ goto theend;
}
}
@@ -409,7 +414,10 @@ static bool do_incsearch_highlighting(int firstc, incsearch_state_T *s,
}
curwin->w_cursor = save_cursor;
- return true;
+ retval = true;
+theend:
+ emsg_off--;
+ return retval;
}
// May do 'incsearch' highlighting if desired.
@@ -549,6 +557,7 @@ static void may_do_incsearch_highlighting(int firstc, long count,
}
update_screen(SOME_VALID);
+ highlight_match = false;
restore_last_search_pattern();
// Leave it at the end to make CTRL-R CTRL-W work. But not when beyond the
@@ -1541,6 +1550,7 @@ static int may_do_command_line_next_incsearch(int firstc, long count,
highlight_match = true;
save_viewstate(&s->old_viewstate);
update_screen(NOT_VALID);
+ highlight_match = false;
redrawcmdline();
curwin->w_cursor = s->match_end;
} else {
@@ -6463,12 +6473,15 @@ static int open_cmdwin(void)
// Save the command line info, can be used recursively.
save_cmdline(&save_ccline);
- /* No Ex mode here! */
+ // No Ex mode here!
exmode_active = 0;
State = NORMAL;
setmouse();
+ // Reset here so it can be set by a CmdWinEnter autocommand.
+ cmdwin_result = 0;
+
// Trigger CmdwinEnter autocommands.
typestr[0] = (char_u)cmdwin_type;
typestr[1] = NUL;
@@ -6484,7 +6497,6 @@ static int open_cmdwin(void)
/*
* Call the main loop until <CR> or CTRL-C is typed.
*/
- cmdwin_result = 0;
normal_enter(true, false);
RedrawingDisabled = i;
diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c
index ba94f55a50..17141f12fd 100644
--- a/src/nvim/extmark.c
+++ b/src/nvim/extmark.c
@@ -568,8 +568,18 @@ void extmark_splice(buf_T *buf,
int new_row, colnr_T new_col, bcount_t new_byte,
ExtmarkOp undo)
{
- long offset = ml_find_line_or_offset(buf, start_row+1, NULL, true);
- extmark_splice_impl(buf, start_row, start_col, offset+start_col,
+ long offset = ml_find_line_or_offset(buf, start_row + 1, NULL, true);
+
+ // On empty buffers, when editing the first line, the line is buffered,
+ // causing offset to be < 0. While the buffer is not actually empty, the
+ // buffered line has not been flushed (and should not be) yet, so the call is
+ // valid but an edge case.
+ //
+ // TODO(vigoux): maybe the is a better way of testing that ?
+ if (offset < 0 && buf->b_ml.ml_chunksize == NULL) {
+ offset = 0;
+ }
+ extmark_splice_impl(buf, start_row, start_col, offset + start_col,
old_row, old_col, old_byte, new_row, new_col, new_byte,
undo);
}
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index 286f2b4fca..ed8b72e2be 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -210,7 +210,8 @@ void filemess(buf_T *buf, char_u *name, char_u *s, int attr)
if (msg_silent != 0) {
return;
}
- add_quoted_fname((char *)IObuff, IOSIZE - 80, buf, (const char *)name);
+ add_quoted_fname((char *)IObuff, IOSIZE - 100, buf, (const char *)name);
+ // Avoid an over-long translation to cause trouble.
xstrlcat((char *)IObuff, (const char *)s, IOSIZE);
// For the first message may have to start a new line.
// For further ones overwrite the previous one, reset msg_scroll before
diff --git a/src/nvim/fold.c b/src/nvim/fold.c
index 9994ad3ea8..9188ae6571 100644
--- a/src/nvim/fold.c
+++ b/src/nvim/fold.c
@@ -280,26 +280,31 @@ int foldLevel(linenr_T lnum)
// Return false if line is not folded.
bool lineFolded(win_T *const win, const linenr_T lnum)
{
- return foldedCount(win, lnum, NULL) != 0;
+ return fold_info(win, lnum).fi_lines != 0;
}
-/* foldedCount() {{{2 */
-/*
- * Count the number of lines that are folded at line number "lnum".
- * Normally "lnum" is the first line of a possible fold, and the returned
- * number is the number of lines in the fold.
- * Doesn't use caching from the displayed window.
- * Returns number of folded lines from "lnum", or 0 if line is not folded.
- * When "infop" is not NULL, fills *infop with the fold level info.
- */
-long foldedCount(win_T *win, linenr_T lnum, foldinfo_T *infop)
+/// fold_info() {{{2
+///
+/// Count the number of lines that are folded at line number "lnum".
+/// Normally "lnum" is the first line of a possible fold, and the returned
+/// number is the number of lines in the fold.
+/// Doesn't use caching from the displayed window.
+///
+/// @return with the fold level info.
+/// fi_lines = number of folded lines from "lnum",
+/// or 0 if line is not folded.
+foldinfo_T fold_info(win_T *win, linenr_T lnum)
{
+ foldinfo_T info;
linenr_T last;
- if (hasFoldingWin(win, lnum, NULL, &last, false, infop)) {
- return (long)(last - lnum + 1);
+ if (hasFoldingWin(win, lnum, NULL, &last, false, &info)) {
+ info.fi_lines = (long)(last - lnum + 1);
+ } else {
+ info.fi_lines = 0;
}
- return 0;
+
+ return info;
}
/* foldmethodIsManual() {{{2 */
@@ -1755,7 +1760,7 @@ static void foldDelMarker(
/// When 'foldtext' isn't set puts the result in "buf[FOLD_TEXT_LEN]".
/// Otherwise the result is in allocated memory.
char_u *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume,
- foldinfo_T *foldinfo, char_u *buf)
+ foldinfo_T foldinfo, char_u *buf)
FUNC_ATTR_NONNULL_ARG(1)
{
char_u *text = NULL;
@@ -1783,11 +1788,12 @@ char_u *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume,
set_vim_var_nr(VV_FOLDSTART, (varnumber_T) lnum);
set_vim_var_nr(VV_FOLDEND, (varnumber_T) lnume);
- /* Set "v:folddashes" to a string of "level" dashes. */
- /* Set "v:foldlevel" to "level". */
- level = foldinfo->fi_level;
- if (level > (int)sizeof(dashes) - 1)
+ // Set "v:folddashes" to a string of "level" dashes.
+ // Set "v:foldlevel" to "level".
+ level = foldinfo.fi_level;
+ if (level > (int)sizeof(dashes) - 1) {
level = (int)sizeof(dashes) - 1;
+ }
memset(dashes, '-', (size_t)level);
dashes[level] = NUL;
set_vim_var_string(VV_FOLDDASHES, dashes, -1);
diff --git a/src/nvim/fold.h b/src/nvim/fold.h
index f35b328fb1..95c4b0c1dc 100644
--- a/src/nvim/fold.h
+++ b/src/nvim/fold.h
@@ -18,6 +18,7 @@ typedef struct foldinfo {
other fields are invalid */
int fi_low_level; /* lowest fold level that starts in the same
line */
+ long fi_lines;
} foldinfo_T;
diff --git a/src/nvim/log.h b/src/nvim/log.h
index 17ff095473..d92b4629ed 100644
--- a/src/nvim/log.h
+++ b/src/nvim/log.h
@@ -5,6 +5,7 @@
#include <stdbool.h>
#include "auto/config.h"
+#include "nvim/macros.h"
#define DEBUG_LOG_LEVEL 0
#define INFO_LOG_LEVEL 1
@@ -68,6 +69,10 @@
# define LOG_CALLSTACK_TO_FILE(fp) log_callstack_to_file(fp, __func__, __LINE__)
#endif
+#if NVIM_HAS_INCLUDE("sanitizer/asan_interface.h")
+# include "sanitizer/asan_interface.h"
+#endif
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "log.h.generated.h"
#endif
diff --git a/src/nvim/macros.h b/src/nvim/macros.h
index 0bbaa87aba..07dcb4a8e8 100644
--- a/src/nvim/macros.h
+++ b/src/nvim/macros.h
@@ -152,6 +152,12 @@
#define STR_(x) #x
#define STR(x) STR_(x)
+#ifndef __has_include
+# define NVIM_HAS_INCLUDE(x) 0
+#else
+# define NVIM_HAS_INCLUDE __has_include
+#endif
+
#ifndef __has_attribute
# define NVIM_HAS_ATTRIBUTE(x) 0
#elif defined(__clang__) && __clang__ == 1 \
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 1f55d2c315..6209dd6492 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -1544,10 +1544,10 @@ int op_delete(oparg_T *oap)
oap->line_count = 0; // no lines deleted
} else if (oap->motion_type == kMTLineWise) {
if (oap->op_type == OP_CHANGE) {
- /* Delete the lines except the first one. Temporarily move the
- * cursor to the next line. Save the current line number, if the
- * last line is deleted it may be changed.
- */
+ // Delete the lines except the first one. Temporarily move the
+ // cursor to the next line. Save the current line number, if the
+ // last line is deleted it may be changed.
+
if (oap->line_count > 1) {
lnum = curwin->w_cursor.lnum;
++curwin->w_cursor.lnum;
@@ -1560,12 +1560,21 @@ int op_delete(oparg_T *oap)
beginline(BL_WHITE); // cursor on first non-white
did_ai = true; // delete the indent when ESC hit
ai_col = curwin->w_cursor.col;
- } else
- beginline(0); /* cursor in column 0 */
- truncate_line(FALSE); /* delete the rest of the line */
- /* leave cursor past last char in line */
- if (oap->line_count > 1)
- u_clearline(); /* "U" command not possible after "2cc" */
+ } else {
+ beginline(0); // cursor in column 0
+ }
+
+ int old_len = (int)STRLEN(ml_get(curwin->w_cursor.lnum));
+ truncate_line(false); // delete the rest of the line
+
+ extmark_splice_cols(curbuf,
+ (int)curwin->w_cursor.lnum-1, curwin->w_cursor.col,
+ old_len - curwin->w_cursor.col, 0, kExtmarkUndo);
+
+ // leave cursor past last char in line
+ if (oap->line_count > 1) {
+ u_clearline(); // "U" command not possible after "2cc"
+ }
} else {
del_lines(oap->line_count, TRUE);
beginline(BL_WHITE | BL_FIX);
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 8d74cead8d..6ae9378236 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -174,6 +174,7 @@ static char_u *p_syn;
static char_u *p_spc;
static char_u *p_spf;
static char_u *p_spl;
+static char_u *p_spo;
static long p_ts;
static long p_tw;
static int p_udf;
@@ -2285,6 +2286,7 @@ void check_buf_options(buf_T *buf)
check_string_option(&buf->b_s.b_p_spc);
check_string_option(&buf->b_s.b_p_spf);
check_string_option(&buf->b_s.b_p_spl);
+ check_string_option(&buf->b_s.b_p_spo);
check_string_option(&buf->b_p_sua);
check_string_option(&buf->b_p_cink);
check_string_option(&buf->b_p_cino);
@@ -3090,6 +3092,10 @@ ambw_end:
} else if (varp == &(curwin->w_s->b_p_spc)) {
// When 'spellcapcheck' is set compile the regexp program.
errmsg = compile_cap_prog(curwin->w_s);
+ } else if (varp == &(curwin->w_s->b_p_spo)) { // 'spelloptions'
+ if (**varp != NUL && STRCMP("camel", *varp) != 0) {
+ errmsg = e_invarg;
+ }
} else if (varp == &p_sps) { // 'spellsuggest'
if (spell_check_sps() != OK) {
errmsg = e_invarg;
@@ -5896,6 +5902,7 @@ static char_u *get_varp(vimoption_T *p)
case PV_SPC: return (char_u *)&(curwin->w_s->b_p_spc);
case PV_SPF: return (char_u *)&(curwin->w_s->b_p_spf);
case PV_SPL: return (char_u *)&(curwin->w_s->b_p_spl);
+ case PV_SPO: return (char_u *)&(curwin->w_s->b_p_spo);
case PV_SW: return (char_u *)&(curbuf->b_p_sw);
case PV_TFU: return (char_u *)&(curbuf->b_p_tfu);
case PV_TS: return (char_u *)&(curbuf->b_p_ts);
@@ -6175,6 +6182,7 @@ void buf_copy_options(buf_T *buf, int flags)
(void)compile_cap_prog(&buf->b_s);
buf->b_s.b_p_spf = vim_strsave(p_spf);
buf->b_s.b_p_spl = vim_strsave(p_spl);
+ buf->b_s.b_p_spo = vim_strsave(p_spo);
buf->b_p_inde = vim_strsave(p_inde);
buf->b_p_indk = vim_strsave(p_indk);
buf->b_p_fp = empty_option;
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index 02fa7ac216..a09811c8fb 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -787,6 +787,7 @@ enum {
, BV_SPC
, BV_SPF
, BV_SPL
+ , BV_SPO
, BV_STS
, BV_SUA
, BV_SW
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index f1221a52a2..02df0ab276 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -2320,6 +2320,16 @@ return {
defaults={if_true={vi="best"}}
},
{
+ full_name='spelloptions', abbreviation='spo',
+ type='string', list='onecomma', scope={'buffer'},
+ deny_duplicates=true,
+ secure=true,
+ vi_def=true,
+ expand=true,
+ varname='p_spo',
+ defaults={if_true={vi="", vim=""}}
+ },
+ {
full_name='splitbelow', abbreviation='sb',
type='bool', scope={'global'},
vi_def=true,
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index 5ac5306dd9..f3fdafcc70 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -133,8 +133,6 @@ static sattr_T *linebuf_attr = NULL;
static match_T search_hl; /* used for 'hlsearch' highlight matching */
-static foldinfo_T win_foldinfo; /* info for 'foldcolumn' */
-
StlClickDefinition *tab_page_click_defs = NULL;
long tab_page_click_defs_size = 0;
@@ -702,7 +700,6 @@ static void win_update(win_T *wp)
long j;
static int recursive = FALSE; /* being called recursively */
int old_botline = wp->w_botline;
- long fold_count;
// Remember what happened to the previous line.
#define DID_NONE 1 // didn't update a line
#define DID_LINE 2 // updated a normal line
@@ -713,6 +710,7 @@ static void win_update(win_T *wp)
linenr_T mod_bot = 0;
int save_got_int;
+
// If we can compute a change in the automatic sizing of the sign column
// under 'signcolumn=auto:X' and signs currently placed in the buffer, better
// figuring it out here so we can redraw the entire screen for it.
@@ -1229,7 +1227,6 @@ static void win_update(win_T *wp)
// Set the time limit to 'redrawtime'.
proftime_T syntax_tm = profile_setlimit(p_rdt);
syn_set_timeout(&syntax_tm);
- win_foldinfo.fi_level = 0;
/*
* Update all the window rows.
@@ -1459,24 +1456,19 @@ static void win_update(win_T *wp)
* Otherwise, display normally (can be several display lines when
* 'wrap' is on).
*/
- fold_count = foldedCount(wp, lnum, &win_foldinfo);
- if (fold_count != 0) {
- fold_line(wp, fold_count, &win_foldinfo, lnum, row);
- ++row;
- --fold_count;
- wp->w_lines[idx].wl_folded = TRUE;
- wp->w_lines[idx].wl_lastlnum = lnum + fold_count;
- did_update = DID_FOLD;
- } else if (idx < wp->w_lines_valid
- && wp->w_lines[idx].wl_valid
- && wp->w_lines[idx].wl_lnum == lnum
- && lnum > wp->w_topline
- && !(dy_flags & (DY_LASTLINE | DY_TRUNCATE))
- && srow + wp->w_lines[idx].wl_size > wp->w_grid.Rows
- && diff_check_fill(wp, lnum) == 0
- ) {
- /* This line is not going to fit. Don't draw anything here,
- * will draw "@ " lines below. */
+ foldinfo_T foldinfo = fold_info(wp, lnum);
+
+ if (foldinfo.fi_lines == 0
+ && idx < wp->w_lines_valid
+ && wp->w_lines[idx].wl_valid
+ && wp->w_lines[idx].wl_lnum == lnum
+ && lnum > wp->w_topline
+ && !(dy_flags & (DY_LASTLINE | DY_TRUNCATE))
+ && srow + wp->w_lines[idx].wl_size > wp->w_grid.Rows
+ && diff_check_fill(wp, lnum) == 0
+ ) {
+ // This line is not going to fit. Don't draw anything here,
+ // will draw "@ " lines below.
row = wp->w_grid.Rows + 1;
} else {
prepare_search_hl(wp, lnum);
@@ -1485,14 +1477,21 @@ static void win_update(win_T *wp)
&& syntax_present(wp))
syntax_end_parsing(syntax_last_parsed + 1);
- /*
- * Display one line.
- */
- row = win_line(wp, lnum, srow, wp->w_grid.Rows, mod_top == 0, false);
+ // Display one line
+ row = win_line(wp, lnum, srow,
+ foldinfo.fi_lines ? srow : wp->w_grid.Rows,
+ mod_top == 0, false, foldinfo);
- wp->w_lines[idx].wl_folded = FALSE;
+ wp->w_lines[idx].wl_folded = foldinfo.fi_lines != 0;
wp->w_lines[idx].wl_lastlnum = lnum;
did_update = DID_LINE;
+
+ if (foldinfo.fi_lines > 0) {
+ did_update = DID_FOLD;
+ foldinfo.fi_lines--;
+ wp->w_lines[idx].wl_lastlnum = lnum + foldinfo.fi_lines;
+ }
+
syntax_last_parsed = lnum;
}
@@ -1507,20 +1506,17 @@ static void win_update(win_T *wp)
idx++;
break;
}
- if (dollar_vcol == -1)
+ if (dollar_vcol == -1) {
wp->w_lines[idx].wl_size = row - srow;
- ++idx;
- lnum += fold_count + 1;
+ }
+ idx++;
+ lnum += foldinfo.fi_lines + 1;
} else {
if (wp->w_p_rnu) {
// 'relativenumber' set: The text doesn't need to be drawn, but
// the number column nearly always does.
- fold_count = foldedCount(wp, lnum, &win_foldinfo);
- if (fold_count != 0) {
- fold_line(wp, fold_count, &win_foldinfo, lnum, row);
- } else {
- (void)win_line(wp, lnum, srow, wp->w_grid.Rows, true, true);
- }
+ foldinfo_T info = fold_info(wp, lnum);
+ (void)win_line(wp, lnum, srow, wp->w_grid.Rows, true, true, info);
}
// This line does not need to be drawn, advance to the next one.
@@ -1752,31 +1748,6 @@ static int advance_color_col(int vcol, int **color_cols)
return **color_cols >= 0;
}
-// Returns the next grid column.
-static int text_to_screenline(win_T *wp, char_u *text, int col, int off)
- FUNC_ATTR_NONNULL_ALL
-{
- int idx = wp->w_p_rl ? off : off + col;
- LineState s = LINE_STATE(text);
-
- while (*s.p != NUL) {
- // TODO(bfredl): cargo-culted from the old Vim code:
- // if(col + cells > wp->w_width - (wp->w_p_rl ? col : 0)) { break; }
- // This is obvious wrong. If Vim ever fixes this, solve for "cells" again
- // in the correct condition.
- const int maxcells = wp->w_grid.Columns - col - (wp->w_p_rl ? col : 0);
- const int cells = line_putchar(&s, &linebuf_char[idx], maxcells,
- wp->w_p_rl);
- if (cells == -1) {
- break;
- }
- col += cells;
- idx += cells;
- }
-
- return col;
-}
-
// Compute the width of the foldcolumn. Based on 'foldcolumn' and how much
// space is available for window "wp", minus "col".
static int compute_foldcolumn(win_T *wp, int col)
@@ -1841,271 +1812,6 @@ static int line_putchar(LineState *s, schar_T *dest, int maxcells, bool rl)
return cells;
}
-/*
- * Display one folded line.
- */
-static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T lnum, int row)
-{
- char_u buf[FOLD_TEXT_LEN];
- pos_T *top, *bot;
- linenr_T lnume = lnum + fold_count - 1;
- int len;
- char_u *text;
- int fdc;
- int col;
- int txtcol;
- int off;
-
- /* Build the fold line:
- * 1. Add the cmdwin_type for the command-line window
- * 2. Add the 'foldcolumn'
- * 3. Add the 'number' or 'relativenumber' column
- * 4. Compose the text
- * 5. Add the text
- * 6. set highlighting for the Visual area an other text
- */
- col = 0;
- off = 0;
-
- /*
- * 1. Add the cmdwin_type for the command-line window
- * Ignores 'rightleft', this window is never right-left.
- */
- if (cmdwin_type != 0 && wp == curwin) {
- schar_from_ascii(linebuf_char[off], cmdwin_type);
- linebuf_attr[off] = win_hl_attr(wp, HLF_AT);
- col++;
- }
-
-# define RL_MEMSET(p, v, l) \
- do { \
- if (wp->w_p_rl) { \
- for (int ri = 0; ri < l; ri++) { \
- linebuf_attr[off + (wp->w_grid.Columns - (p) - (l)) + ri] = v; \
- } \
- } else { \
- for (int ri = 0; ri < l; ri++) { \
- linebuf_attr[off + (p) + ri] = v; \
- } \
- } \
- } while (0)
-
- // 2. Add the 'foldcolumn'
- // Reduce the width when there is not enough space.
- fdc = compute_foldcolumn(wp, col);
- if (fdc > 0) {
- fill_foldcolumn(buf, wp, true, lnum);
- const char_u *it = &buf[0];
- for (int i = 0; i < fdc; i++) {
- int mb_c = mb_ptr2char_adv(&it);
- if (wp->w_p_rl) {
- schar_from_char(linebuf_char[off + wp->w_grid.Columns - i - 1 - col],
- mb_c);
- } else {
- schar_from_char(linebuf_char[off + col + i], mb_c);
- }
- }
- RL_MEMSET(col, win_hl_attr(wp, HLF_FC), fdc);
- col += fdc;
- }
-
- /* Set all attributes of the 'number' or 'relativenumber' column and the
- * text */
- RL_MEMSET(col, win_hl_attr(wp, HLF_FL), wp->w_grid.Columns - col);
-
- // If signs are being displayed, add spaces.
- if (win_signcol_count(wp) > 0) {
- len = wp->w_grid.Columns - col;
- if (len > 0) {
- int len_max = win_signcol_width(wp) * win_signcol_count(wp);
- if (len > len_max) {
- len = len_max;
- }
- char_u space_buf[18] = " ";
- assert((size_t)len_max <= sizeof(space_buf));
- copy_text_attr(off + col, space_buf, len,
- win_hl_attr(wp, HLF_FL));
- col += len;
- }
- }
-
- /*
- * 3. Add the 'number' or 'relativenumber' column
- */
- if (wp->w_p_nu || wp->w_p_rnu) {
- len = wp->w_grid.Columns - col;
- if (len > 0) {
- int w = number_width(wp);
- long num;
- char *fmt = "%*ld ";
-
- if (len > w + 1)
- len = w + 1;
-
- if (wp->w_p_nu && !wp->w_p_rnu)
- /* 'number' + 'norelativenumber' */
- num = (long)lnum;
- else {
- /* 'relativenumber', don't use negative numbers */
- num = labs((long)get_cursor_rel_lnum(wp, lnum));
- if (num == 0 && wp->w_p_nu && wp->w_p_rnu) {
- /* 'number' + 'relativenumber': cursor line shows absolute
- * line number */
- num = lnum;
- fmt = "%-*ld ";
- }
- }
-
- snprintf((char *)buf, FOLD_TEXT_LEN, fmt, w, num);
- if (wp->w_p_rl) {
- // the line number isn't reversed
- copy_text_attr(off + wp->w_grid.Columns - len - col, buf, len,
- win_hl_attr(wp, HLF_FL));
- } else {
- copy_text_attr(off + col, buf, len, win_hl_attr(wp, HLF_FL));
- }
- col += len;
- }
- }
-
- /*
- * 4. Compose the folded-line string with 'foldtext', if set.
- */
- text = get_foldtext(wp, lnum, lnume, foldinfo, buf);
-
- txtcol = col; /* remember where text starts */
-
- // 5. move the text to linebuf_char[off]. Fill up with "fold".
- // Right-left text is put in columns 0 - number-col, normal text is put
- // in columns number-col - window-width.
- col = text_to_screenline(wp, text, col, off);
-
- /* Fill the rest of the line with the fold filler */
- if (wp->w_p_rl)
- col -= txtcol;
-
- schar_T sc;
- schar_from_char(sc, wp->w_p_fcs_chars.fold);
- while (col < wp->w_grid.Columns
- - (wp->w_p_rl ? txtcol : 0)
- ) {
- schar_copy(linebuf_char[off+col++], sc);
- }
-
- if (text != buf)
- xfree(text);
-
- /*
- * 6. set highlighting for the Visual area an other text.
- * If all folded lines are in the Visual area, highlight the line.
- */
- if (VIsual_active && wp->w_buffer == curwin->w_buffer) {
- if (ltoreq(curwin->w_cursor, VIsual)) {
- /* Visual is after curwin->w_cursor */
- top = &curwin->w_cursor;
- bot = &VIsual;
- } else {
- /* Visual is before curwin->w_cursor */
- top = &VIsual;
- bot = &curwin->w_cursor;
- }
- if (lnum >= top->lnum
- && lnume <= bot->lnum
- && (VIsual_mode != 'v'
- || ((lnum > top->lnum
- || (lnum == top->lnum
- && top->col == 0))
- && (lnume < bot->lnum
- || (lnume == bot->lnum
- && (bot->col - (*p_sel == 'e'))
- >= (colnr_T)STRLEN(ml_get_buf(wp->w_buffer, lnume,
- FALSE))))))) {
- if (VIsual_mode == Ctrl_V) {
- // Visual block mode: highlight the chars part of the block
- if (wp->w_old_cursor_fcol + txtcol < (colnr_T)wp->w_grid.Columns) {
- if (wp->w_old_cursor_lcol != MAXCOL
- && wp->w_old_cursor_lcol + txtcol
- < (colnr_T)wp->w_grid.Columns) {
- len = wp->w_old_cursor_lcol;
- } else {
- len = wp->w_grid.Columns - txtcol;
- }
- RL_MEMSET(wp->w_old_cursor_fcol + txtcol, win_hl_attr(wp, HLF_V),
- len - (int)wp->w_old_cursor_fcol);
- }
- } else {
- // Set all attributes of the text
- RL_MEMSET(txtcol, win_hl_attr(wp, HLF_V), wp->w_grid.Columns - txtcol);
- }
- }
- }
-
- // Show colorcolumn in the fold line, but let cursorcolumn override it.
- if (wp->w_p_cc_cols) {
- int i = 0;
- int j = wp->w_p_cc_cols[i];
- int old_txtcol = txtcol;
-
- while (j > -1) {
- txtcol += j;
- if (wp->w_p_wrap) {
- txtcol -= wp->w_skipcol;
- } else {
- txtcol -= wp->w_leftcol;
- }
- if (txtcol >= 0 && txtcol < wp->w_grid.Columns) {
- linebuf_attr[off + txtcol] =
- hl_combine_attr(linebuf_attr[off + txtcol], win_hl_attr(wp, HLF_MC));
- }
- txtcol = old_txtcol;
- j = wp->w_p_cc_cols[++i];
- }
- }
-
- /* Show 'cursorcolumn' in the fold line. */
- if (wp->w_p_cuc) {
- txtcol += wp->w_virtcol;
- if (wp->w_p_wrap)
- txtcol -= wp->w_skipcol;
- else
- txtcol -= wp->w_leftcol;
- if (txtcol >= 0 && txtcol < wp->w_grid.Columns) {
- linebuf_attr[off + txtcol] = hl_combine_attr(
- linebuf_attr[off + txtcol], win_hl_attr(wp, HLF_CUC));
- }
- }
-
- grid_put_linebuf(&wp->w_grid, row, 0, wp->w_grid.Columns, wp->w_grid.Columns,
- false, wp, wp->w_hl_attr_normal, false);
-
- /*
- * Update w_cline_height and w_cline_folded if the cursor line was
- * updated (saves a call to plines() later).
- */
- if (wp == curwin
- && lnum <= curwin->w_cursor.lnum
- && lnume >= curwin->w_cursor.lnum) {
- curwin->w_cline_row = row;
- curwin->w_cline_height = 1;
- curwin->w_cline_folded = true;
- curwin->w_valid |= (VALID_CHEIGHT|VALID_CROW);
- conceal_cursor_used = conceal_cursor_line(curwin);
- }
-}
-
-
-/// Copy "buf[len]" to linebuf_char["off"] and set attributes to "attr".
-///
-/// Only works for ASCII text!
-static void copy_text_attr(int off, char_u *buf, int len, int attr)
-{
- int i;
-
- for (i = 0; i < len; i++) {
- schar_from_ascii(linebuf_char[off + i], buf[i]);
- linebuf_attr[off + i] = attr;
- }
-}
/// Fills the foldcolumn at "p" for window "wp".
/// Only to be called when 'foldcolumn' > 0.
@@ -2120,7 +1826,7 @@ static size_t
fill_foldcolumn(
char_u *p,
win_T *wp,
- int closed,
+ foldinfo_T foldinfo,
linenr_T lnum
)
{
@@ -2131,10 +1837,11 @@ fill_foldcolumn(
size_t char_counter = 0;
int symbol = 0;
int len = 0;
+ bool closed = foldinfo.fi_lines > 0;
// Init to all spaces.
memset(p, ' ', MAX_MCO * fdc + 1);
- level = win_foldinfo.fi_level;
+ level = foldinfo.fi_level;
// If the column is too narrow, we start at the lowest level that
// fits and use numbers to indicated the depth.
@@ -2144,8 +1851,8 @@ fill_foldcolumn(
}
for (i = 0; i < MIN(fdc, level); i++) {
- if (win_foldinfo.fi_lnum == lnum
- && first_level + i >= win_foldinfo.fi_low_level) {
+ if (foldinfo.fi_lnum == lnum
+ && first_level + i >= foldinfo.fi_low_level) {
symbol = wp->w_p_fcs_chars.foldopen;
} else if (first_level == 1) {
symbol = wp->w_p_fcs_chars.foldsep;
@@ -2176,21 +1883,27 @@ fill_foldcolumn(
return MAX(char_counter + (fdc-i), (size_t)fdc);
}
-/*
- * Display line "lnum" of window 'wp' on the screen.
- * Start at row "startrow", stop when "endrow" is reached.
- * wp->w_virtcol needs to be valid.
- *
- * Return the number of last row the line occupies.
- */
+
+/// Display line "lnum" of window 'wp' on the screen.
+/// Start at row "startrow", stop when "endrow" is reached.
+/// wp->w_virtcol needs to be valid.
+///
+/// @param lnum line to display
+/// @param endrow stop drawing once reaching this row
+/// @param nochange not updating for changed text
+/// @param number_only only update the number column
+/// @param foldinfo fold info for this line
+///
+/// @return the number of last row the line occupies.
static int
win_line (
win_T *wp,
linenr_T lnum,
int startrow,
int endrow,
- bool nochange, // not updating for changed text
- bool number_only // only update the number column
+ bool nochange,
+ bool number_only,
+ foldinfo_T foldinfo
)
{
int c = 0; // init for GCC
@@ -2295,6 +2008,8 @@ win_line (
bool has_decorations = false; // this buffer has decorations
bool do_virttext = false; // draw virtual text for this line
+ char_u buf_fold[FOLD_TEXT_LEN + 1]; // Hold value returned by get_foldtext
+
/* draw_state: items that are drawn in sequence: */
#define WL_START 0 /* nothing done yet */
# define WL_CMDLINE WL_START + 1 /* cmdline window column */
@@ -2846,7 +2561,7 @@ win_line (
// already be in use.
xfree(p_extra_free);
p_extra_free = xmalloc(MAX_MCO * fdc + 1);
- n_extra = fill_foldcolumn(p_extra_free, wp, false, lnum);
+ n_extra = fill_foldcolumn(p_extra_free, wp, foldinfo, lnum);
p_extra_free[n_extra] = NUL;
p_extra = p_extra_free;
c_extra = NUL;
@@ -3072,6 +2787,41 @@ win_line (
break;
}
+ if (draw_state == WL_LINE
+ && foldinfo.fi_level != 0
+ && foldinfo.fi_lines > 0
+ && vcol == 0
+ && n_extra == 0
+ && row == startrow) {
+ char_attr = win_hl_attr(wp, HLF_FL);
+
+ linenr_T lnume = lnum + foldinfo.fi_lines - 1;
+ memset(buf_fold, ' ', FOLD_TEXT_LEN);
+ p_extra = get_foldtext(wp, lnum, lnume, foldinfo, buf_fold);
+ n_extra = STRLEN(p_extra);
+
+ if (p_extra != buf_fold) {
+ xfree(p_extra_free);
+ p_extra_free = p_extra;
+ }
+ c_extra = NUL;
+ c_final = NUL;
+ p_extra[n_extra] = NUL;
+ }
+
+ if (draw_state == WL_LINE
+ && foldinfo.fi_level != 0
+ && foldinfo.fi_lines > 0
+ && col < grid->Columns
+ && n_extra == 0
+ && row == startrow) {
+ // fill rest of line with 'fold'
+ c_extra = wp->w_p_fcs_chars.fold;
+ c_final = NUL;
+
+ n_extra = wp->w_p_rl ? (col + 1) : (grid->Columns - col);
+ }
+
if (draw_state == WL_LINE && (area_highlighting || has_spell)) {
// handle Visual or match highlighting in this line
if (vcol == fromcol
@@ -3300,6 +3050,10 @@ win_line (
p_extra++;
}
n_extra--;
+ } else if (foldinfo.fi_lines > 0) {
+ // skip writing the buffer line itself
+ c = NUL;
+ XFREE_CLEAR(p_extra_free);
} else {
int c0;
@@ -3868,7 +3622,7 @@ win_line (
// not showing the '>', put pointer back to avoid getting stuck
ptr++;
}
- }
+ } // end of printing from buffer content
/* In the cursor line and we may be concealing characters: correct
* the cursor column when we reach its position. */
@@ -4178,11 +3932,10 @@ win_line (
if (wp == curwin && lnum == curwin->w_cursor.lnum) {
curwin->w_cline_row = startrow;
curwin->w_cline_height = row - startrow;
- curwin->w_cline_folded = false;
+ curwin->w_cline_folded = foldinfo.fi_lines > 0;
curwin->w_valid |= (VALID_CHEIGHT|VALID_CROW);
conceal_cursor_used = conceal_cursor_line(curwin);
}
-
break;
}
@@ -4371,6 +4124,7 @@ win_line (
* so far. If there is no more to display it is caught above.
*/
if ((wp->w_p_rl ? (col < 0) : (col >= grid->Columns))
+ && foldinfo.fi_lines == 0
&& (*ptr != NUL
|| filler_todo > 0
|| (wp->w_p_list && wp->w_p_lcs_chars.eol != NUL
diff --git a/src/nvim/spell.c b/src/nvim/spell.c
index dc1bfe25b4..f036d7fe04 100644
--- a/src/nvim/spell.c
+++ b/src/nvim/spell.c
@@ -362,6 +362,8 @@ size_t spell_check(
size_t wrongcaplen = 0;
int lpi;
bool count_word = docount;
+ bool use_camel_case = *wp->w_s->b_p_spo != NUL;
+ bool camel_case = false;
// A word never starts at a space or a control character. Return quickly
// then, skipping over the character.
@@ -394,9 +396,24 @@ size_t spell_check(
mi.mi_word = ptr;
mi.mi_fend = ptr;
if (spell_iswordp(mi.mi_fend, wp)) {
+ int prev_upper;
+ int this_upper;
+
+ if (use_camel_case) {
+ c = PTR2CHAR(mi.mi_fend);
+ this_upper = SPELL_ISUPPER(c);
+ }
+
do {
MB_PTR_ADV(mi.mi_fend);
- } while (*mi.mi_fend != NUL && spell_iswordp(mi.mi_fend, wp));
+ if (use_camel_case) {
+ prev_upper = this_upper;
+ c = PTR2CHAR(mi.mi_fend);
+ this_upper = SPELL_ISUPPER(c);
+ camel_case = !prev_upper && this_upper;
+ }
+ } while (*mi.mi_fend != NUL && spell_iswordp(mi.mi_fend, wp)
+ && !camel_case);
if (capcol != NULL && *capcol == 0 && wp->w_s->b_cap_prog != NULL) {
// Check word starting with capital letter.
@@ -428,6 +445,11 @@ size_t spell_check(
(void)spell_casefold(ptr, (int)(mi.mi_fend - ptr), mi.mi_fword, MAXWLEN + 1);
mi.mi_fwordlen = (int)STRLEN(mi.mi_fword);
+ if (camel_case) {
+ // introduce a fake word end space into the folded word.
+ mi.mi_fword[mi.mi_fwordlen - 1] = ' ';
+ }
+
// The word is bad unless we recognize it.
mi.mi_result = SP_BAD;
mi.mi_result2 = SP_BAD;
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index 4aa7c21ce4..9a9cc45c6b 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -5588,9 +5588,11 @@ void ex_ownsyntax(exarg_T *eap)
hash_init(&curwin->w_s->b_keywtab_ic);
// TODO: Keep the spell checking as it was. NOLINT(readability/todo)
curwin->w_p_spell = false; // No spell checking
+ // make sure option values are "empty_option" instead of NULL
clear_string_option(&curwin->w_s->b_p_spc);
clear_string_option(&curwin->w_s->b_p_spf);
clear_string_option(&curwin->w_s->b_p_spl);
+ clear_string_option(&curwin->w_s->b_p_spo);
clear_string_option(&curwin->w_s->b_syn_isk);
}
diff --git a/src/nvim/testdir/check.vim b/src/nvim/testdir/check.vim
index 073873bcb0..e0ebe8fd49 100644
--- a/src/nvim/testdir/check.vim
+++ b/src/nvim/testdir/check.vim
@@ -65,3 +65,11 @@ func CheckCanRunGui()
throw 'Skipped: cannot start the GUI'
endif
endfunc
+
+" Command to check that not currently using the GUI
+command CheckNotGui call CheckNotGui()
+func CheckNotGui()
+ if has('gui_running')
+ throw 'Skipped: only works in the terminal'
+ endif
+endfunc
diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim
index 3dd68873d4..094bb3ebd1 100644
--- a/src/nvim/testdir/test_autocmd.vim
+++ b/src/nvim/testdir/test_autocmd.vim
@@ -1,6 +1,8 @@
" Tests for autocommands
source shared.vim
+source check.vim
+source term_util.vim
func! s:cleanup_buffers() abort
for bnr in range(1, bufnr('$'))
@@ -1735,6 +1737,35 @@ func Test_throw_in_BufWritePre()
au! throwing
endfunc
+func Test_autocmd_CmdWinEnter()
+ CheckRunVimInTerminal
+ " There is not cmdwin switch, so
+ " test for cmdline_hist
+ " (both are available with small builds)
+ CheckFeature cmdline_hist
+ let lines =<< trim END
+ let b:dummy_var = 'This is a dummy'
+ autocmd CmdWinEnter * quit
+ let winnr = winnr('$')
+ END
+ let filename='XCmdWinEnter'
+ call writefile(lines, filename)
+ let buf = RunVimInTerminal('-S '.filename, #{rows: 6})
+
+ call term_sendkeys(buf, "q:")
+ call term_wait(buf)
+ call term_sendkeys(buf, ":echo b:dummy_var\<cr>")
+ call WaitForAssert({-> assert_match('^This is a dummy', term_getline(buf, 6))}, 1000)
+ call term_sendkeys(buf, ":echo &buftype\<cr>")
+ call WaitForAssert({-> assert_notmatch('^nofile', term_getline(buf, 6))}, 1000)
+ call term_sendkeys(buf, ":echo winnr\<cr>")
+ call WaitForAssert({-> assert_match('^1', term_getline(buf, 6))}, 1000)
+
+ " clean up
+ call StopVimInTerminal(buf)
+ call delete(filename)
+endfunc
+
func Test_FileChangedShell_reload()
if !has('unix')
return
diff --git a/src/nvim/testdir/test_edit.vim b/src/nvim/testdir/test_edit.vim
index 12d5d9790e..7f456ffbce 100644
--- a/src/nvim/testdir/test_edit.vim
+++ b/src/nvim/testdir/test_edit.vim
@@ -1,9 +1,11 @@
" Test for edit functions
-"
+
if exists("+t_kD")
let &t_kD="[3;*~"
endif
+source check.vim
+
" Needed for testing basic rightleft: Test_edit_rightleft
source view_util.vim
@@ -733,17 +735,16 @@ func! Test_edit_CTRL_O()
endfunc
func! Test_edit_CTRL_R()
- throw 'skipped: Nvim does not support test_override()'
" Insert Register
new
- call test_override("ALL", 1)
+ " call test_override("ALL", 1)
set showcmd
call feedkeys("AFOOBAR eins zwei\<esc>", 'tnix')
call feedkeys("O\<c-r>.", 'tnix')
call feedkeys("O\<c-r>=10*500\<cr>\<esc>", 'tnix')
call feedkeys("O\<c-r>=getreg('=', 1)\<cr>\<esc>", 'tnix')
call assert_equal(["getreg('=', 1)", '5000', "FOOBAR eins zwei", "FOOBAR eins zwei"], getline(1, '$'))
- call test_override("ALL", 0)
+ " call test_override("ALL", 0)
set noshowcmd
bw!
endfunc
@@ -955,7 +956,6 @@ func! Test_edit_DROP()
endfunc
func! Test_edit_CTRL_V()
- throw 'skipped: Nvim does not support test_override()'
if has("ebcdic")
return
endif
@@ -965,7 +965,7 @@ func! Test_edit_CTRL_V()
" force some redraws
set showmode showcmd
"call test_override_char_avail(1)
- call test_override('ALL', 1)
+ " call test_override('ALL', 1)
call feedkeys("A\<c-v>\<c-n>\<c-v>\<c-l>\<c-v>\<c-b>\<esc>", 'tnix')
call assert_equal(["abc\x0e\x0c\x02"], getline(1, '$'))
@@ -978,7 +978,7 @@ func! Test_edit_CTRL_V()
set norl
endif
- call test_override('ALL', 0)
+ " call test_override('ALL', 0)
set noshowmode showcmd
bw!
endfunc
@@ -1514,3 +1514,23 @@ func Test_edit_startinsert()
set backspace&
bwipe!
endfunc
+
+func Test_edit_noesckeys()
+ CheckNotGui
+ new
+
+ " <Left> moves cursor when 'esckeys' is set
+ exe "set t_kl=\<Esc>OD"
+ " set esckeys
+ call feedkeys("axyz\<Esc>ODX", "xt")
+ " call assert_equal("xyXz", getline(1))
+
+ " <Left> exits Insert mode when 'esckeys' is off
+ " set noesckeys
+ call setline(1, '')
+ call feedkeys("axyz\<Esc>ODX", "xt")
+ call assert_equal(["DX", "xyz"], getline(1, 2))
+
+ bwipe!
+ " set esckeys
+endfunc
diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim
index 617e3dfe41..9f8939f2f6 100644
--- a/src/nvim/testdir/test_filetype.vim
+++ b/src/nvim/testdir/test_filetype.vim
@@ -326,7 +326,7 @@ let s:filename_checks = {
\ 'pamconf': ['/etc/pam.conf'],
\ 'pamenv': ['/etc/security/pam_env.conf', '/home/user/.pam_environment'],
\ 'papp': ['file.papp', 'file.pxml', 'file.pxsl'],
- \ 'pascal': ['file.pas', 'file.dpr'],
+ \ 'pascal': ['file.pas', 'file.pp', 'file.dpr', 'file.lpr'],
\ 'passwd': ['any/etc/passwd', 'any/etc/passwd-', 'any/etc/passwd.edit', 'any/etc/shadow', 'any/etc/shadow-', 'any/etc/shadow.edit', 'any/var/backups/passwd.bak', 'any/var/backups/shadow.bak'],
\ 'pccts': ['file.g'],
\ 'pdf': ['file.pdf'],
@@ -455,7 +455,7 @@ let s:filename_checks = {
\ 'texmf': ['texmf.cnf'],
\ 'text': ['file.text', 'README'],
\ 'tf': ['file.tf', '.tfrc', 'tfrc'],
- \ 'tidy': ['.tidyrc', 'tidyrc'],
+ \ 'tidy': ['.tidyrc', 'tidyrc', 'tidy.conf'],
\ 'tilde': ['file.t.html'],
\ 'tli': ['file.tli'],
\ 'tmux': ['tmuxfile.conf', '.tmuxfile.conf'],
diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim
index 6b45ac61d1..8fa70a5313 100644
--- a/src/nvim/testdir/test_functions.vim
+++ b/src/nvim/testdir/test_functions.vim
@@ -1221,6 +1221,24 @@ func Test_reg_executing_and_recording()
unlet s:reg_stat
endfunc
+func Test_getchar()
+ call feedkeys('a', '')
+ call assert_equal(char2nr('a'), getchar())
+
+ " call test_setmouse(1, 3)
+ " let v:mouse_win = 9
+ " let v:mouse_winid = 9
+ " let v:mouse_lnum = 9
+ " let v:mouse_col = 9
+ " call feedkeys("\<S-LeftMouse>", '')
+ call nvim_input_mouse('left', 'press', 'S', 0, 0, 2)
+ call assert_equal("\<S-LeftMouse>", getchar())
+ call assert_equal(1, v:mouse_win)
+ call assert_equal(win_getid(1), v:mouse_winid)
+ call assert_equal(1, v:mouse_lnum)
+ call assert_equal(3, v:mouse_col)
+endfunc
+
func Test_libcall_libcallnr()
if !has('libcall')
return
@@ -1341,3 +1359,22 @@ func Test_readdir()
call delete('Xdir', 'rf')
endfunc
+
+" Test for the eval() function
+func Test_eval()
+ call assert_fails("call eval('5 a')", 'E488:')
+endfunc
+
+" Test for the nr2char() function
+func Test_nr2char()
+ " set encoding=latin1
+ call assert_equal('@', nr2char(64))
+ set encoding=utf8
+ call assert_equal('a', nr2char(97, 1))
+ call assert_equal('a', nr2char(97, 0))
+
+ call assert_equal("\x80\xfc\b\xf4\x80\xfeX\x80\xfeX\x80\xfeX", eval('"\<M-' .. nr2char(0x100000) .. '>"'))
+ call assert_equal("\x80\xfc\b\xfd\x80\xfeX\x80\xfeX\x80\xfeX\x80\xfeX\x80\xfeX", eval('"\<M-' .. nr2char(0x40000000) .. '>"'))
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_gf.vim b/src/nvim/testdir/test_gf.vim
index 4a4ffcefa1..ee548037ba 100644
--- a/src/nvim/testdir/test_gf.vim
+++ b/src/nvim/testdir/test_gf.vim
@@ -1,3 +1,4 @@
+" Test for the gf and gF (goto file) commands
" This is a test if a URL is recognized by "gf", with the cursor before and
" after the "://". Also test ":\\".
@@ -109,7 +110,7 @@ func Test_gf()
endfunc
func Test_gf_visual()
- call writefile([], "Xtest_gf_visual")
+ call writefile(['one', 'two', 'three', 'four'], "Xtest_gf_visual")
new
call setline(1, 'XXXtest_gf_visualXXX')
set hidden
@@ -118,6 +119,30 @@ func Test_gf_visual()
norm! ttvtXgf
call assert_equal('Xtest_gf_visual', bufname('%'))
+ " if multiple lines are selected, then gf should fail
+ call setline(1, ["one", "two"])
+ normal VGgf
+ call assert_equal('Xtest_gf_visual', @%)
+
+ " following line number is used for gF
+ bwipe!
+ new
+ call setline(1, 'XXXtest_gf_visual:3XXX')
+ norm! 0ttvt:gF
+ call assert_equal('Xtest_gf_visual', bufname('%'))
+ call assert_equal(3, getcurpos()[1])
+
+ " line number in visual area is used for file name
+ if has('unix')
+ bwipe!
+ call writefile([], "Xtest_gf_visual:3")
+ new
+ call setline(1, 'XXXtest_gf_visual:3XXX')
+ norm! 0ttvtXgF
+ call assert_equal('Xtest_gf_visual:3', bufname('%'))
+ call delete('Xtest_gf_visual:3')
+ endif
+
bwipe!
call delete('Xtest_gf_visual')
set hidden&
diff --git a/src/nvim/testdir/test_search.vim b/src/nvim/testdir/test_search.vim
index e3a0fce5a6..211fc73562 100644
--- a/src/nvim/testdir/test_search.vim
+++ b/src/nvim/testdir/test_search.vim
@@ -708,6 +708,19 @@ func Test_incsearch_substitute_dump()
call VerifyScreenDump(buf, 'Test_incsearch_substitute_12', {})
call term_sendkeys(buf, "\<Esc>")
call VerifyScreenDump(buf, 'Test_incsearch_substitute_13', {})
+ call term_sendkeys(buf, ":%bwipe!\<CR>")
+ call term_sendkeys(buf, ":only!\<CR>")
+
+ " get :'<,'>s command in history
+ call term_sendkeys(buf, ":set cmdheight=2\<CR>")
+ call term_sendkeys(buf, "aasdfasdf\<Esc>")
+ call term_sendkeys(buf, "V:s/a/b/g\<CR>")
+ " Using '<,'> does not give E20
+ call term_sendkeys(buf, ":new\<CR>")
+ call term_sendkeys(buf, "aasdfasdf\<Esc>")
+ call term_sendkeys(buf, ":\<Up>\<Up>")
+ call VerifyScreenDump(buf, 'Test_incsearch_substitute_14', {})
+ call term_sendkeys(buf, "<Esc>")
call StopVimInTerminal(buf)
call delete('Xis_subst_script')
@@ -730,11 +743,14 @@ func Test_incsearch_sort_dump()
" the 'ambiwidth' check.
sleep 100m
- " Need to send one key at a time to force a redraw.
call term_sendkeys(buf, ':sort ni u /on')
call VerifyScreenDump(buf, 'Test_incsearch_sort_01', {})
call term_sendkeys(buf, "\<Esc>")
+ call term_sendkeys(buf, ':sort! /on')
+ call VerifyScreenDump(buf, 'Test_incsearch_sort_02', {})
+ call term_sendkeys(buf, "\<Esc>")
+
call StopVimInTerminal(buf)
call delete('Xis_sort_script')
endfunc
diff --git a/src/nvim/testdir/test_spell.vim b/src/nvim/testdir/test_spell.vim
index 414c7278eb..64e030426d 100644
--- a/src/nvim/testdir/test_spell.vim
+++ b/src/nvim/testdir/test_spell.vim
@@ -79,6 +79,11 @@ func Test_spellbadword()
call assert_equal(['bycycle', 'bad'], spellbadword('My bycycle.'))
call assert_equal(['another', 'caps'], spellbadword('A sentence. another sentence'))
+ call assert_equal(['TheCamelWord', 'bad'], spellbadword('TheCamelWord asdf'))
+ set spelloptions=camel
+ call assert_equal(['asdf', 'bad'], spellbadword('TheCamelWord asdf'))
+ set spelloptions=
+
set spelllang=en
call assert_equal(['', ''], spellbadword('centre'))
call assert_equal(['', ''], spellbadword('center'))
diff --git a/src/nvim/testdir/test_syntax.vim b/src/nvim/testdir/test_syntax.vim
index 85ee42420e..2404f113d9 100644
--- a/src/nvim/testdir/test_syntax.vim
+++ b/src/nvim/testdir/test_syntax.vim
@@ -369,7 +369,11 @@ func Test_ownsyntax()
call setline(1, '#define FOO')
syntax on
set filetype=c
+
ownsyntax perl
+ " this should not crash
+ set
+
call assert_equal('perlComment', synIDattr(synID(line('.'), col('.'), 1), 'name'))
call assert_equal('c', b:current_syntax)
call assert_equal('perl', w:current_syntax)
diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim
index cffd80ff4f..d5ea54b764 100644
--- a/src/nvim/testdir/test_timers.vim
+++ b/src/nvim/testdir/test_timers.vim
@@ -233,16 +233,17 @@ func Test_timer_catch_error()
endfunc
func FeedAndPeek(timer)
- call test_feedinput('a')
+ " call test_feedinput('a')
+ call nvim_input('a')
call getchar(1)
endfunc
func Interrupt(timer)
- call test_feedinput("\<C-C>")
+ " call test_feedinput("\<C-C>")
+ call nvim_input("\<C-C>")
endfunc
func Test_peek_and_get_char()
- throw 'skipped: Nvim does not support test_feedinput()'
if !has('unix') && !has('gui_running')
return
endif
diff --git a/src/nvim/window.c b/src/nvim/window.c
index cec0dfd67f..6608deb231 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -6019,6 +6019,12 @@ char_u *grab_file_name(long count, linenr_T *file_lnum)
char_u *ptr;
if (get_visual_text(NULL, &ptr, &len) == FAIL)
return NULL;
+ // Only recognize ":123" here
+ if (file_lnum != NULL && ptr[len] == ':' && isdigit(ptr[len + 1])) {
+ char_u *p = ptr + len + 1;
+
+ *file_lnum = getdigits_long(&p, false, 0);
+ }
return find_file_name_in_path(ptr, len, options, count, curbuf->b_ffname);
}
return file_name_at_cursor(options | FNAME_HYP, count, file_lnum);