diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/nvim/buffer.c | 37 | ||||
| -rw-r--r-- | src/nvim/buffer_defs.h | 5 | ||||
| -rw-r--r-- | src/nvim/eval.c | 3 | ||||
| -rw-r--r-- | src/nvim/eval/typval_encode.c.h | 2 | ||||
| -rw-r--r-- | src/nvim/fileio.c | 32 | ||||
| -rw-r--r-- | src/nvim/main.c | 60 | ||||
| -rw-r--r-- | src/nvim/main.h | 34 | ||||
| -rw-r--r-- | src/nvim/ops.c | 10 | ||||
| -rw-r--r-- | src/nvim/option.c | 31 | ||||
| -rw-r--r-- | src/nvim/regexp_nfa.c | 17 | ||||
| -rw-r--r-- | src/nvim/search.c | 17 | ||||
| -rw-r--r-- | src/nvim/spell.c | 2 | ||||
| -rw-r--r-- | src/nvim/syntax.c | 75 | ||||
| -rw-r--r-- | src/nvim/terminal.c | 12 | ||||
| -rw-r--r-- | src/nvim/testdir/summarize.vim | 2 | ||||
| -rw-r--r-- | src/nvim/testdir/test_autocmd.vim | 4 | ||||
| -rw-r--r-- | src/nvim/testdir/test_filetype.vim | 31 | ||||
| -rw-r--r-- | src/nvim/testdir/test_increment.vim | 36 | ||||
| -rw-r--r-- | src/nvim/testdir/test_regexp_utf8.vim | 19 | ||||
| -rw-r--r-- | src/nvim/testdir/test_syntax.vim | 85 | ||||
| -rw-r--r-- | src/nvim/testdir/test_tagjump.vim | 69 | ||||
| -rw-r--r-- | src/nvim/testdir/test_textobjects.vim | 11 |
22 files changed, 474 insertions, 120 deletions
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 3ce39feda5..29712e9f52 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -3084,28 +3084,29 @@ fileinfo( } vim_snprintf_add((char *)buffer, IOSIZE, "\"%s%s%s%s%s%s", - curbufIsChanged() ? (shortmess(SHM_MOD) - ? " [+]" : _(" [Modified]")) : " ", - (curbuf->b_flags & BF_NOTEDITED) - && !bt_dontwrite(curbuf) - ? _("[Not edited]") : "", - (curbuf->b_flags & BF_NEW) - && !bt_dontwrite(curbuf) - ? _("[New file]") : "", - (curbuf->b_flags & BF_READERR) ? _("[Read errors]") : "", - curbuf->b_p_ro ? (shortmess(SHM_RO) ? _("[RO]") - : _("[readonly]")) : "", - (curbufIsChanged() || (curbuf->b_flags & BF_WRITE_MASK) - || curbuf->b_p_ro) ? - " " : ""); - /* With 32 bit longs and more than 21,474,836 lines multiplying by 100 - * causes an overflow, thus for large numbers divide instead. */ - if (curwin->w_cursor.lnum > 1000000L) + curbufIsChanged() + ? (shortmess(SHM_MOD) ? " [+]" : _(" [Modified]")) : " ", + (curbuf->b_flags & BF_NOTEDITED) && !bt_dontwrite(curbuf) + ? _("[Not edited]") : "", + (curbuf->b_flags & BF_NEW) && !bt_dontwrite(curbuf) + ? new_file_message() : "", + (curbuf->b_flags & BF_READERR) + ? _("[Read errors]") : "", + curbuf->b_p_ro + ? (shortmess(SHM_RO) ? _("[RO]") : _("[readonly]")) : "", + (curbufIsChanged() + || (curbuf->b_flags & BF_WRITE_MASK) + || curbuf->b_p_ro) + ? " " : ""); + // With 32 bit longs and more than 21,474,836 lines multiplying by 100 + // causes an overflow, thus for large numbers divide instead. + if (curwin->w_cursor.lnum > 1000000L) { n = (int)(((long)curwin->w_cursor.lnum) / ((long)curbuf->b_ml.ml_line_count / 100L)); - else + } else { n = (int)(((long)curwin->w_cursor.lnum * 100L) / (long)curbuf->b_ml.ml_line_count); + } if (curbuf->b_ml.ml_flags & ML_EMPTY) { vim_snprintf_add((char *)buffer, IOSIZE, "%s", _(no_lines_msg)); } else if (p_ru) { diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index d696eedbb7..550f8a5e40 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -373,6 +373,10 @@ struct stl_hlrec { #define SYNSPL_TOP 1 // spell check toplevel text #define SYNSPL_NOTOP 2 // don't spell check toplevel text +// values for b_syn_foldlevel: how to compute foldlevel on a line +#define SYNFLD_START 0 // use level of item at start of line +#define SYNFLD_MINIMUM 1 // use lowest local minimum level on line + // avoid #ifdefs for when b_spell is not available # define B_SPELL(buf) ((buf)->b_spell) @@ -398,6 +402,7 @@ typedef struct { int b_syn_error; // TRUE when error occurred in HL bool b_syn_slow; // true when 'redrawtime' reached int b_syn_ic; // ignore case for :syn cmds + int b_syn_foldlevel; // how to compute foldlevel on a line int b_syn_spell; // SYNSPL_ values garray_T b_syn_patterns; // table for syntax patterns garray_T b_syn_clusters; // table for syntax clusters diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 66cd0e09c6..b7e827e86b 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -8137,9 +8137,6 @@ void set_argv_var(char **argv, int argc) list_T *l = tv_list_alloc(argc); int i; - if (l == NULL) { - getout(1); - } tv_list_set_lock(l, VAR_FIXED); for (i = 0; i < argc; i++) { tv_list_append_string(l, (const char *const)argv[i], -1); diff --git a/src/nvim/eval/typval_encode.c.h b/src/nvim/eval/typval_encode.c.h index 0aa64b1d5f..91c948ce7e 100644 --- a/src/nvim/eval/typval_encode.c.h +++ b/src/nvim/eval/typval_encode.c.h @@ -364,7 +364,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( _TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(tv->vval.v_list, lv_copyID, copyID, kMPConvList); TYPVAL_ENCODE_CONV_LIST_START(tv, tv_list_len(tv->vval.v_list)); - assert(saved_copyID != copyID && saved_copyID != copyID - 1); + assert(saved_copyID != copyID); _mp_push(*mpstack, ((MPConvStackVal) { .type = kMPConvList, .tv = tv, diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index f29304867a..20f0cdccc3 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -569,20 +569,21 @@ readfile( return FAIL; } } - if (dir_of_file_exists(fname)) - filemess(curbuf, sfname, (char_u *)_("[New File]"), 0); - else - filemess(curbuf, sfname, - (char_u *)_("[New DIRECTORY]"), 0); - /* Even though this is a new file, it might have been - * edited before and deleted. Get the old marks. */ + if (dir_of_file_exists(fname)) { + filemess(curbuf, sfname, (char_u *)new_file_message(), 0); + } else { + filemess(curbuf, sfname, (char_u *)_("[New DIRECTORY]"), 0); + } + // Even though this is a new file, it might have been + // edited before and deleted. Get the old marks. check_marks_read(); - /* Set forced 'fileencoding'. */ - if (eap != NULL) + // Set forced 'fileencoding'. + if (eap != NULL) { set_forced_fenc(eap); + } apply_autocmds_exarg(EVENT_BUFNEWFILE, sfname, sfname, - FALSE, curbuf, eap); - /* remember the current fileformat */ + false, curbuf, eap); + // remember the current fileformat save_file_ff(curbuf); if (aborting()) /* autocmds may abort script processing */ @@ -2203,6 +2204,11 @@ static void check_marks_read(void) curbuf->b_marks_read = true; } +char *new_file_message(void) +{ + return shortmess(SHM_NEW) ? _("[New]") : _("[New File]"); +} + /* * buf_write() - write to file "fname" lines "start" through "end" * @@ -3513,8 +3519,8 @@ restore_backup: STRCAT(IObuff, _("[Device]")); c = TRUE; } else if (newfile) { - STRCAT(IObuff, shortmess(SHM_NEW) ? _("[New]") : _("[New File]")); - c = TRUE; + STRCAT(IObuff, new_file_message()); + c = true; } if (no_eol) { msg_add_eol(); diff --git a/src/nvim/main.c b/src/nvim/main.c index b3a903cbe3..ae64046d07 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -84,44 +84,11 @@ #endif #include "nvim/api/vim.h" -// Maximum number of commands from + or -c arguments. -#define MAX_ARG_CMDS 10 - // values for "window_layout" #define WIN_HOR 1 // "-o" horizontally split windows #define WIN_VER 2 // "-O" vertically split windows #define WIN_TABS 3 // "-p" windows on tab pages -// Struct for various parameters passed between main() and other functions. -typedef struct { - int argc; - char **argv; - - char *use_vimrc; // vimrc from -u argument - - int n_commands; // no. of commands from + or -c - char *commands[MAX_ARG_CMDS]; // commands from + or -c arg - char_u cmds_tofree[MAX_ARG_CMDS]; // commands that need free() - int n_pre_commands; // no. of commands from --cmd - char *pre_commands[MAX_ARG_CMDS]; // commands from --cmd argument - - int edit_type; // type of editing to do - char_u *tagname; // tag from -t argument - char_u *use_ef; // 'errorfile' from -q argument - - bool input_isatty; // stdin is a terminal - bool output_isatty; // stdout is a terminal - bool err_isatty; // stderr is a terminal - int no_swap_file; // "-n" argument used - int use_debug_break_level; - int window_count; // number of windows to use - int window_layout; // 0, WIN_HOR, WIN_VER or WIN_TABS - - int diff_mode; // start with 'diff' set - - char *listen_addr; // --listen {address} -} mparm_T; - // Values for edit_type. #define EDIT_NONE 0 // no edit type yet #define EDIT_FILE 1 // file name argument[s] given, use argument list @@ -188,7 +155,7 @@ bool event_teardown(void) /// Performs early initialization. /// /// Needed for unit tests. Must be called after `time_init()`. -void early_init(void) +void early_init(mparm_T *paramp) { env_init(); fs_init(); @@ -222,7 +189,7 @@ void early_init(void) // msg_outtrans_len_attr(). // First find out the home directory, needed to expand "~" in options. init_homedir(); // find real value of $HOME - set_init_1(); + set_init_1(paramp != NULL ? paramp->clean : false); log_init(); TIME_MSG("inits 1"); @@ -265,9 +232,17 @@ int main(int argc, char **argv) init_startuptime(¶ms); + // Need to find "--clean" before actually parsing arguments. + for (int i = 1; i < params.argc; i++) { + if (STRICMP(params.argv[i], "--clean") == 0) { + params.clean = true; + break; + } + } + event_init(); - early_init(); + early_init(¶ms); set_argv_var(argv, argc); // set v:argv @@ -291,6 +266,12 @@ int main(int argc, char **argv) fname = get_fname(¶ms, cwd); } + // Recovery mode without a file name: List swap files. + // In this case, no UI is needed. + if (recoverymode && fname == NULL) { + headless_mode = true; + } + TIME_MSG("expanding arguments"); if (params.diff_mode && params.window_count == -1) @@ -862,6 +843,7 @@ static void command_line_scan(mparm_T *parmp) argv_idx += 11; } else if (STRNICMP(argv[0] + argv_idx, "clean", 5) == 0) { parmp->use_vimrc = "NONE"; + parmp->clean = true; set_option_value("shadafile", 0L, "NONE", 0); } else { if (argv[0][argv_idx]) @@ -978,7 +960,6 @@ static void command_line_scan(mparm_T *parmp) case 'r': // "-r" recovery mode case 'L': { // "-L" recovery mode recoverymode = 1; - headless_mode = true; break; } case 's': { @@ -1277,9 +1258,8 @@ static void init_params(mparm_T *paramp, int argc, char **argv) /// Initialize global startuptime file if "--startuptime" passed as an argument. static void init_startuptime(mparm_T *paramp) { - for (int i = 1; i < paramp->argc; i++) { - if (STRICMP(paramp->argv[i], "--startuptime") == 0 - && i + 1 < paramp->argc) { + for (int i = 1; i < paramp->argc - 1; i++) { + if (STRICMP(paramp->argv[i], "--startuptime") == 0) { time_fd = os_fopen(paramp->argv[i + 1], "a"); time_start("--- NVIM STARTING ---"); break; diff --git a/src/nvim/main.h b/src/nvim/main.h index 86d25fe657..61252f2bce 100644 --- a/src/nvim/main.h +++ b/src/nvim/main.h @@ -4,8 +4,42 @@ #include "nvim/normal.h" #include "nvim/event/loop.h" +// Maximum number of commands from + or -c arguments. +#define MAX_ARG_CMDS 10 + extern Loop main_loop; +// Struct for various parameters passed between main() and other functions. +typedef struct { + int argc; + char **argv; + + char *use_vimrc; // vimrc from -u argument + bool clean; // --clean argument + + int n_commands; // no. of commands from + or -c + char *commands[MAX_ARG_CMDS]; // commands from + or -c arg + char_u cmds_tofree[MAX_ARG_CMDS]; // commands that need free() + int n_pre_commands; // no. of commands from --cmd + char *pre_commands[MAX_ARG_CMDS]; // commands from --cmd argument + + int edit_type; // type of editing to do + char_u *tagname; // tag from -t argument + char_u *use_ef; // 'errorfile' from -q argument + + bool input_isatty; // stdin is a terminal + bool output_isatty; // stdout is a terminal + bool err_isatty; // stderr is a terminal + int no_swap_file; // "-n" argument used + int use_debug_break_level; + int window_count; // number of windows to use + int window_layout; // 0, WIN_HOR, WIN_VER or WIN_TABS + + int diff_mode; // start with 'diff' set + + char *listen_addr; // --listen {address} +} mparm_T; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "main.h.generated.h" #endif diff --git a/src/nvim/ops.c b/src/nvim/ops.c index eb32a1dd9b..e905029dae 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -4671,17 +4671,23 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) int maxlen = 0; pos_T startpos; pos_T endpos; + colnr_T save_coladd = 0; dohex = (vim_strchr(curbuf->b_p_nf, 'x') != NULL); // "heX" dooct = (vim_strchr(curbuf->b_p_nf, 'o') != NULL); // "Octal" dobin = (vim_strchr(curbuf->b_p_nf, 'b') != NULL); // "Bin" doalp = (vim_strchr(curbuf->b_p_nf, 'p') != NULL); // "alPha" + if (virtual_active()) { + save_coladd = pos->coladd; + pos->coladd = 0; + } + curwin->w_cursor = *pos; ptr = ml_get(pos->lnum); col = pos->col; - if (*ptr == NUL) { + if (*ptr == NUL || col + !!save_coladd >= (int)STRLEN(ptr)) { goto theend; } @@ -4976,6 +4982,8 @@ theend: curwin->w_cursor = save_cursor; } else if (did_change) { curwin->w_set_curswant = true; + } else if (virtual_active()) { + curwin->w_cursor.coladd = save_coladd; } return did_change; diff --git a/src/nvim/option.c b/src/nvim/option.c index 96f8e1529a..d789ad3587 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -524,11 +524,17 @@ char *get_lib_dir(void) /// /// Windows: Uses "…/nvim-data" for kXDGDataHome to avoid storing /// configuration and data files in the same path. #4403 -static void set_runtimepath_default(void) +/// +/// If "clean_arg" is true, Nvim was started with --clean. +static void set_runtimepath_default(bool clean_arg) { size_t rtp_size = 0; - char *const data_home = stdpaths_get_xdg_var(kXDGDataHome); - char *const config_home = stdpaths_get_xdg_var(kXDGConfigHome); + char *const data_home = clean_arg + ? NULL + : stdpaths_get_xdg_var(kXDGDataHome); + char *const config_home = clean_arg + ? NULL + : stdpaths_get_xdg_var(kXDGConfigHome); char *const vimruntime = vim_getenv("VIMRUNTIME"); char *const libdir = get_lib_dir(); char *const data_dirs = stdpaths_get_xdg_var(kXDGDataDirs); @@ -622,7 +628,8 @@ static void set_runtimepath_default(void) /// Initialize the options, first part. /// /// Called only once from main(), just after creating the first buffer. -void set_init_1(void) +/// If "clean_arg" is true, Nvim was started with --clean. +void set_init_1(bool clean_arg) { int opt_idx; @@ -765,7 +772,7 @@ void set_init_1(void) true); // Set default for &runtimepath. All necessary expansions are performed in // this function. - set_runtimepath_default(); + set_runtimepath_default(clean_arg); /* * Set all the options (except the terminal options) to their default @@ -2538,8 +2545,8 @@ static bool valid_filetype(const char_u *val) return valid_name(val, ".-_"); } -/// Return true if "val" is a valid 'spellang' value. -bool valid_spellang(const char_u *val) +/// Return true if "val" is a valid 'spelllang' value. +bool valid_spelllang(const char_u *val) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { return valid_name(val, ".-_,@"); @@ -3071,7 +3078,7 @@ ambw_end: const bool is_spellfile = varp == &(curwin->w_s->b_p_spf); if ((is_spellfile && !valid_spellfile(*varp)) - || (!is_spellfile && !valid_spellang(*varp))) { + || (!is_spellfile && !valid_spelllang(*varp))) { errmsg = e_invarg; } else { errmsg = did_set_spell_option(is_spellfile); @@ -5469,9 +5476,6 @@ static int put_setstring(FILE *fd, char *cmd, char *name, // replace home directory in the whole option value into "buf" buf = xmalloc(size); - if (buf == NULL) { - goto fail; - } home_replace(NULL, *valuep, buf, size, false); // If the option value is longer than MAXPATHL, we need to append @@ -5479,10 +5483,7 @@ static int put_setstring(FILE *fd, char *cmd, char *name, // can be expanded when read back. if (size >= MAXPATHL && (flags & P_COMMA) != 0 && vim_strchr(*valuep, ',') != NULL) { - part = xmalloc(size); - if (part == NULL) { - goto fail; - } + part = xmalloc(size); // write line break to clear the option, e.g. ':set rtp=' if (put_eol(fd) == FAIL) { diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c index 2ca5f42e51..387732fdee 100644 --- a/src/nvim/regexp_nfa.c +++ b/src/nvim/regexp_nfa.c @@ -4960,7 +4960,7 @@ static long find_match_text(colnr_T startcol, int regstart, char_u *match_text) int c2_len = PTR2LEN(s2); int c2 = PTR2CHAR(s2); - if ((c1 != c2 && (!rex.reg_ic || mb_tolower(c1) != mb_tolower(c2))) + if ((c1 != c2 && (!rex.reg_ic || utf_fold(c1) != utf_fold(c2))) || c1_len != c2_len) { match = false; break; @@ -5682,11 +5682,11 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, break; } if (rex.reg_ic) { - int curc_low = mb_tolower(curc); + int curc_low = utf_fold(curc); int done = false; for (; c1 <= c2; c1++) { - if (mb_tolower(c1) == curc_low) { + if (utf_fold(c1) == curc_low) { result = result_if_matched; done = TRUE; break; @@ -5698,8 +5698,8 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, } } else if (state->c < 0 ? check_char_class(state->c, curc) : (curc == state->c - || (rex.reg_ic && mb_tolower(curc) - == mb_tolower(state->c)))) { + || (rex.reg_ic + && utf_fold(curc) == utf_fold(state->c)))) { result = result_if_matched; break; } @@ -6106,7 +6106,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, result = (c == curc); if (!result && rex.reg_ic) { - result = mb_tolower(c) == mb_tolower(curc); + result = utf_fold(c) == utf_fold(curc); } // If rex.reg_icombine is not set only skip over the character @@ -6260,8 +6260,9 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, // Checking if the required start character matches is // cheaper than adding a state that won't match. c = PTR2CHAR(reginput + clen); - if (c != prog->regstart && (!rex.reg_ic || mb_tolower(c) - != mb_tolower(prog->regstart))) { + if (c != prog->regstart + && (!rex.reg_ic + || utf_fold(c) != utf_fold(prog->regstart))) { #ifdef REGEXP_DEBUG fprintf(log_fd, " Skipping start state, regstart does not match\n"); diff --git a/src/nvim/search.c b/src/nvim/search.c index 23086c629b..b105d99d7c 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -2326,9 +2326,9 @@ int findsent(Direction dir, long count) func = decl; while (count--) { - /* - * if on an empty line, skip up to a non-empty line - */ + const pos_T prev_pos = pos; + + // if on an empty line, skip up to a non-empty line if (gchar_pos(&pos) == NUL) { do { if ((*func)(&pos) == -1) { @@ -2411,6 +2411,17 @@ found: while (!noskip && ((c = gchar_pos(&pos)) == ' ' || c == '\t')) if (incl(&pos) == -1) break; + + if (equalpos(prev_pos, pos)) { + // didn't actually move, advance one character and try again + if ((*func)(&pos) == -1) { + if (count) { + return FAIL; + } + break; + } + count++; + } } setpcmark(); diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 180073ade1..4d8da1ba14 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -2007,7 +2007,7 @@ char_u *did_set_spelllang(win_T *wp) region = NULL; len = (int)STRLEN(lang); - if (!valid_spellang(lang)) { + if (!valid_spelllang(lang)) { continue; } diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index f3b05c3961..4aa7c21ce4 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -121,6 +121,8 @@ static int hl_attr_table[] = { HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERCURL, HL_ITALIC, HL_INVERSE, HL_INVERSE, HL_STRIKETHROUGH, HL_NOCOMBINE, 0 }; +static char e_illegal_arg[] = N_("E390: Illegal argument: %s"); + // The patterns that are being searched for are stored in a syn_pattern. // A match item consists of one pattern. // A start/end item consists of n start patterns and m end patterns. @@ -3045,7 +3047,7 @@ static void syn_cmd_conceal(exarg_T *eap, int syncing) } else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3) { curwin->w_s->b_syn_conceal = false; } else { - EMSG2(_("E390: Illegal argument: %s"), arg); + EMSG2(_(e_illegal_arg), arg); } } @@ -3073,7 +3075,42 @@ static void syn_cmd_case(exarg_T *eap, int syncing) } else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6) { curwin->w_s->b_syn_ic = true; } else { - EMSG2(_("E390: Illegal argument: %s"), arg); + EMSG2(_(e_illegal_arg), arg); + } +} + +/// Handle ":syntax foldlevel" command. +static void syn_cmd_foldlevel(exarg_T *eap, int syncing) +{ + char_u *arg = eap->arg; + char_u *arg_end; + + eap->nextcmd = find_nextcmd(arg); + if (eap->skip) + return; + + if (*arg == NUL) { + switch (curwin->w_s->b_syn_foldlevel) { + case SYNFLD_START: MSG(_("syntax foldlevel start")); break; + case SYNFLD_MINIMUM: MSG(_("syntax foldlevel minimum")); break; + default: break; + } + return; + } + + arg_end = skiptowhite(arg); + if (STRNICMP(arg, "start", 5) == 0 && arg_end - arg == 5) { + curwin->w_s->b_syn_foldlevel = SYNFLD_START; + } else if (STRNICMP(arg, "minimum", 7) == 0 && arg_end - arg == 7) { + curwin->w_s->b_syn_foldlevel = SYNFLD_MINIMUM; + } else { + EMSG2(_(e_illegal_arg), arg); + return; + } + + arg = skipwhite(arg_end); + if (*arg != NUL) { + EMSG2(_(e_illegal_arg), arg); } } @@ -3105,7 +3142,7 @@ static void syn_cmd_spell(exarg_T *eap, int syncing) } else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7) { curwin->w_s->b_syn_spell = SYNSPL_DEFAULT; } else { - EMSG2(_("E390: Illegal argument: %s"), arg); + EMSG2(_(e_illegal_arg), arg); return; } @@ -3161,6 +3198,7 @@ void syntax_clear(synblock_T *block) block->b_syn_error = false; // clear previous error block->b_syn_slow = false; // clear previous timeout block->b_syn_ic = false; // Use case, by default + block->b_syn_foldlevel = SYNFLD_START; block->b_syn_spell = SYNSPL_DEFAULT; // default spell checking block->b_syn_containedin = false; block->b_syn_conceal = false; @@ -5485,6 +5523,7 @@ static struct subcommand subcommands[] = { "cluster", syn_cmd_cluster }, { "conceal", syn_cmd_conceal }, { "enable", syn_cmd_enable }, + { "foldlevel", syn_cmd_foldlevel }, { "include", syn_cmd_include }, { "iskeyword", syn_cmd_iskeyword }, { "keyword", syn_cmd_keyword }, @@ -5763,6 +5802,17 @@ int syn_get_stack_item(int i) return CUR_STATE(i).si_id; } +static int syn_cur_foldlevel(void) +{ + int level = 0; + for (int i = 0; i < current_state.ga_len; i++) { + if (CUR_STATE(i).si_flags & HL_FOLD) { + level++; + } + } + return level; +} + /* * Function called to get folding level for line "lnum" in window "wp". */ @@ -5776,9 +5826,22 @@ int syn_get_foldlevel(win_T *wp, long lnum) && !wp->w_s->b_syn_slow) { syntax_start(wp, lnum); - for (int i = 0; i < current_state.ga_len; ++i) { - if (CUR_STATE(i).si_flags & HL_FOLD) { - ++level; + // Start with the fold level at the start of the line. + level = syn_cur_foldlevel(); + + if (wp->w_s->b_syn_foldlevel == SYNFLD_MINIMUM) { + // Find the lowest fold level that is followed by a higher one. + int cur_level = level; + int low_level = cur_level; + while (!current_finished) { + (void)syn_current_attr(false, false, NULL, false); + cur_level = syn_cur_foldlevel(); + if (cur_level < low_level) { + low_level = cur_level; + } else if (cur_level > low_level) { + level = low_level; + } + current_col++; } } } diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index a096b77ac6..6a13341a89 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -489,7 +489,17 @@ static int terminal_execute(VimState *state, int key) terminal_send_key(s->term, key); } - return curbuf->handle == s->term->buf_handle; + if (curbuf->terminal == NULL) { + return 0; + } + if (s->term != curbuf->terminal) { + invalidate_terminal(s->term, s->term->cursor.row, s->term->cursor.row + 1); + invalidate_terminal(curbuf->terminal, + curbuf->terminal->cursor.row, + curbuf->terminal->cursor.row + 1); + s->term = curbuf->terminal; + } + return 1; } void terminal_destroy(Terminal *term) diff --git a/src/nvim/testdir/summarize.vim b/src/nvim/testdir/summarize.vim index 4e4135287b..7f8f758a71 100644 --- a/src/nvim/testdir/summarize.vim +++ b/src/nvim/testdir/summarize.vim @@ -28,7 +28,7 @@ if 1 " This uses the :s command to just fetch and process the output of the " tests, it doesn't actually replace anything. " And it uses "silent" to avoid reporting the number of matches. - silent %s/^Executed\s\+\zs\d\+\ze\s\+tests\?/\=Count(submatch(0),'executed')/egn + silent %s/Executed\s\+\zs\d\+\ze\s\+tests\?/\=Count(submatch(0),'executed')/egn silent %s/^SKIPPED \zs.*/\=Count(submatch(0), 'skipped')/egn silent %s/^\(\d\+\)\s\+FAILED:/\=Count(submatch(1), 'failed')/egn diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index 5217aa7339..d116246ef3 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -1252,6 +1252,10 @@ func Test_TextYankPost() call assert_equal( \{'regcontents': ['foo'], 'inclusive': v:false, 'regname': '', 'operator': 'y', 'visual': v:false, 'regtype': 'V'}, \g:event) + norm Vy + call assert_equal( + \{'regcontents': ['foo'], 'inclusive': v:true, 'regname': '', 'operator': 'y', 'visual': v:true, 'regtype': 'V'}, + \g:event) call feedkeys("\<C-V>y", 'x') call assert_equal( \{'regcontents': ['f'], 'inclusive': v:true, 'regname': '', 'operator': 'y', 'visual': v:true, 'regtype': "\x161"}, diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index ffd2cee80f..9303fac927 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -131,7 +131,7 @@ let s:filename_checks = { \ 'def': ['file.def'], \ 'denyhosts': ['denyhosts.conf'], \ 'desc': ['file.desc'], - \ 'desktop': ['file.desktop', '.directory'], + \ 'desktop': ['file.desktop', '.directory', 'file.directory'], \ 'dictconf': ['dict.conf', '.dictrc'], \ 'dictdconf': ['dictd.conf'], \ 'diff': ['file.diff', 'file.rej'], @@ -329,7 +329,7 @@ let s:filename_checks = { \ 'pccts': ['file.g'], \ 'pdf': ['file.pdf'], \ 'perl': ['file.plx', 'file.al', 'file.psgi', 'gitolite.rc', '.gitolite.rc', 'example.gitolite.rc'], - \ 'perl6': ['file.p6', 'file.pm6', 'file.pl6'], + \ 'perl6': ['file.p6', 'file.pm6', 'file.pl6', 'file.raku', 'file.rakumod'], \ 'pf': ['pf.conf'], \ 'pfmain': ['main.cf'], \ 'php': ['file.php', 'file.php9', 'file.phtml', 'file.ctp'], @@ -360,7 +360,7 @@ let s:filename_checks = { \ 'protocols': ['/etc/protocols'], \ 'psf': ['file.psf'], \ 'pyrex': ['file.pyx', 'file.pxd'], - \ 'python': ['file.py', 'file.pyw', '.pythonstartup', '.pythonrc', 'file.ptl', 'file.pyi'], + \ 'python': ['file.py', 'file.pyw', '.pythonstartup', '.pythonrc', 'file.ptl', 'file.pyi', 'SConstruct'], \ 'quake': ['anybaseq2/file.cfg', 'anyid1/file.cfg', 'quake3/file.cfg'], \ 'radiance': ['file.rad', 'file.mat'], \ 'ratpoison': ['.ratpoisonrc', 'ratpoisonrc'], @@ -427,8 +427,8 @@ let s:filename_checks = { \ 'sqr': ['file.sqr', 'file.sqi'], \ 'squid': ['squid.conf'], \ 'srec': ['file.s19', 'file.s28', 'file.s37', 'file.mot', 'file.srec'], - \ 'sshconfig': ['ssh_config', '/.ssh/config'], - \ 'sshdconfig': ['sshd_config'], + \ 'sshconfig': ['ssh_config', '/.ssh/config', '/etc/ssh/ssh_config.d/file.conf', 'any/etc/ssh/ssh_config.d/file.conf'], + \ 'sshdconfig': ['sshd_config', '/etc/ssh/sshd_config.d/file.conf', 'any/etc/ssh/sshd_config.d/file.conf'], \ 'st': ['file.st'], \ 'stata': ['file.ado', 'file.do', 'file.imata', 'file.mata'], \ 'stp': ['file.stp'], @@ -439,7 +439,7 @@ let s:filename_checks = { \ 'swiftgyb': ['file.swift.gyb'], \ 'sil': ['file.sil'], \ 'sysctl': ['/etc/sysctl.conf', '/etc/sysctl.d/file.conf'], - \ 'systemd': ['any/systemd/file.automount', 'any/systemd/file.mount', 'any/systemd/file.path', 'any/systemd/file.service', 'any/systemd/file.socket', 'any/systemd/file.swap', 'any/systemd/file.target', 'any/systemd/file.timer', '/etc/systemd/system/some.d/file.conf', '/etc/systemd/system/some.d/.#file', '/etc/systemd/system/.#otherfile', '/home/user/.config/systemd/user/some.d/mine.conf', '/home/user/.config/systemd/user/some.d/.#file', '/home/user/.config/systemd/user/.#otherfile'], + \ 'systemd': ['any/systemd/file.automount', 'any/systemd/file.dnssd', 'any/systemd/file.link', 'any/systemd/file.mount', 'any/systemd/file.netdev', 'any/systemd/file.network', 'any/systemd/file.nspawn', 'any/systemd/file.path', 'any/systemd/file.service', 'any/systemd/file.slice', 'any/systemd/file.socket', 'any/systemd/file.swap', 'any/systemd/file.target', 'any/systemd/file.timer', '/etc/systemd/some.conf.d/file.conf', '/etc/systemd/system/some.d/file.conf', '/etc/systemd/system/some.d/.#file', '/etc/systemd/system/.#otherfile', '/home/user/.config/systemd/user/some.d/mine.conf', '/home/user/.config/systemd/user/some.d/.#file', '/home/user/.config/systemd/user/.#otherfile'], \ 'systemverilog': ['file.sv', 'file.svh'], \ 'tags': ['tags'], \ 'tak': ['file.tak'], @@ -606,9 +606,19 @@ let s:script_checks = { \ 'yaml': [['%YAML 1.2']], \ } -func Test_script_detection() +" Various forms of "env" optional arguments. +let s:script_env_checks = { + \ 'perl': [['#!/usr/bin/env VAR=val perl']], + \ 'scala': [['#!/usr/bin/env VAR=val VVAR=vval scala']], + \ 'awk': [['#!/usr/bin/env VAR=val -i awk']], + \ 'scheme': [['#!/usr/bin/env VAR=val --ignore-environment scheme']], + \ 'python': [['#!/usr/bin/env VAR=val -S python -w -T']], + \ 'wml': [['#!/usr/bin/env VAR=val --split-string wml']], + \ } + +func Run_script_detection(test_dict) filetype on - for [ft, files] in items(s:script_checks) + for [ft, files] in items(a:test_dict) for file in files call writefile(file, 'Xtest') split Xtest @@ -620,6 +630,11 @@ func Test_script_detection() filetype off endfunc +func Test_script_detection() + call Run_script_detection(s:script_checks) + call Run_script_detection(s:script_env_checks) +endfunc + func Test_setfiletype_completion() call feedkeys(":setfiletype java\<C-A>\<C-B>\"\<CR>", 'tx') call assert_equal('"setfiletype java javacc javascript javascriptreact', @:) diff --git a/src/nvim/testdir/test_increment.vim b/src/nvim/testdir/test_increment.vim index ab11d943d9..f81f8edbde 100644 --- a/src/nvim/testdir/test_increment.vim +++ b/src/nvim/testdir/test_increment.vim @@ -779,4 +779,40 @@ func Test_increment_empty_line() bwipe! endfunc +func Test_normal_increment_with_virtualedit() + set virtualedit=all + + call setline(1, ["\<TAB>1"]) + exec "norm! 0\<C-A>" + call assert_equal("\<TAB>2", getline(1)) + call assert_equal([0, 1, 2, 0], getpos('.')) + + call setline(1, ["\<TAB>1"]) + exec "norm! 0l\<C-A>" + call assert_equal("\<TAB>2", getline(1)) + call assert_equal([0, 1, 2, 0], getpos('.')) + + call setline(1, ["\<TAB>1"]) + exec "norm! 07l\<C-A>" + call assert_equal("\<TAB>2", getline(1)) + call assert_equal([0, 1, 2, 0], getpos('.')) + + call setline(1, ["\<TAB>1"]) + exec "norm! 0w\<C-A>" + call assert_equal("\<TAB>2", getline(1)) + call assert_equal([0, 1, 2, 0], getpos('.')) + + call setline(1, ["\<TAB>1"]) + exec "norm! 0wl\<C-A>" + call assert_equal("\<TAB>1", getline(1)) + call assert_equal([0, 1, 3, 0], getpos('.')) + + call setline(1, ["\<TAB>1"]) + exec "norm! 0w30l\<C-A>" + call assert_equal("\<TAB>1", getline(1)) + call assert_equal([0, 1, 3, 29], getpos('.')) + + set virtualedit& +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_regexp_utf8.vim b/src/nvim/testdir/test_regexp_utf8.vim index ecd0e8d56b..f48458566b 100644 --- a/src/nvim/testdir/test_regexp_utf8.vim +++ b/src/nvim/testdir/test_regexp_utf8.vim @@ -332,4 +332,23 @@ func Test_ambiwidth() set regexpengine& ambiwidth& endfunc +func Run_regexp_ignore_case() + call assert_equal('iIİ', substitute('iIİ', '\([iIİ]\)', '\1', 'g')) + + call assert_equal('iIx', substitute('iIİ', '\c\([İ]\)', 'x', 'g')) + call assert_equal('xxİ', substitute('iIİ', '\(i\c\)', 'x', 'g')) + call assert_equal('iIx', substitute('iIİ', '\(İ\c\)', 'x', 'g')) + call assert_equal('iIx', substitute('iIİ', '\c\(\%u0130\)', 'x', 'g')) + call assert_equal('iIx', substitute('iIİ', '\c\([\u0130]\)', 'x', 'g')) + call assert_equal('iIx', substitute('iIİ', '\c\([\u012f-\u0131]\)', 'x', 'g')) +endfunc + +func Test_regexp_ignore_case() + set regexpengine=1 + call Run_regexp_ignore_case() + set regexpengine=2 + call Run_regexp_ignore_case() + set regexpengine& +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_syntax.vim b/src/nvim/testdir/test_syntax.vim index 6cada1503f..85ee42420e 100644 --- a/src/nvim/testdir/test_syntax.vim +++ b/src/nvim/testdir/test_syntax.vim @@ -153,7 +153,7 @@ endfunc func Test_syntax_completion() call feedkeys(":syn \<C-A>\<C-B>\"\<CR>", 'tx') - call assert_equal('"syn case clear cluster conceal enable include iskeyword keyword list manual match off on region reset spell sync', @:) + call assert_equal('"syn case clear cluster conceal enable foldlevel include iskeyword keyword list manual match off on region reset spell sync', @:) call feedkeys(":syn case \<C-A>\<C-B>\"\<CR>", 'tx') call assert_equal('"syn case ignore match', @:) @@ -579,3 +579,86 @@ func Test_syntax_hangs() set redrawtime& bwipe! endfunc + +func Test_syntax_foldlevel() + new + call setline(1, [ + \ 'void f(int a)', + \ '{', + \ ' if (a == 1) {', + \ ' a = 0;', + \ ' } else if (a == 2) {', + \ ' a = 1;', + \ ' } else {', + \ ' a = 2;', + \ ' }', + \ ' if (a > 0) {', + \ ' if (a == 1) {', + \ ' a = 0;', + \ ' } /* missing newline */ } /* end of outer if */ else {', + \ ' a = 1;', + \ ' }', + \ ' if (a == 1)', + \ ' {', + \ ' a = 0;', + \ ' }', + \ ' else if (a == 2)', + \ ' {', + \ ' a = 1;', + \ ' }', + \ ' else', + \ ' {', + \ ' a = 2;', + \ ' }', + \ '}', + \ ]) + setfiletype c + syntax on + set foldmethod=syntax + + call assert_fails('syn foldlevel start start', 'E390') + call assert_fails('syn foldlevel not_an_option', 'E390') + + set foldlevel=1 + + syn foldlevel start + redir @c + syn foldlevel + redir END + call assert_equal("\nsyntax foldlevel start", @c) + syn sync fromstart + let a = map(range(3,9), 'foldclosed(v:val)') + call assert_equal([3,3,3,3,3,3,3], a) " attached cascade folds together + let a = map(range(10,15), 'foldclosed(v:val)') + call assert_equal([10,10,10,10,10,10], a) " over-attached 'else' hidden + let a = map(range(16,27), 'foldclosed(v:val)') + let unattached_results = [-1,17,17,17,-1,21,21,21,-1,25,25,25] + call assert_equal(unattached_results, a) " unattached cascade folds separately + + syn foldlevel minimum + redir @c + syn foldlevel + redir END + call assert_equal("\nsyntax foldlevel minimum", @c) + syn sync fromstart + let a = map(range(3,9), 'foldclosed(v:val)') + call assert_equal([3,3,5,5,7,7,7], a) " attached cascade folds separately + let a = map(range(10,15), 'foldclosed(v:val)') + call assert_equal([10,10,10,13,13,13], a) " over-attached 'else' visible + let a = map(range(16,27), 'foldclosed(v:val)') + call assert_equal(unattached_results, a) " unattached cascade folds separately + + set foldlevel=2 + + syn foldlevel start + syn sync fromstart + let a = map(range(11,14), 'foldclosed(v:val)') + call assert_equal([11,11,11,-1], a) " over-attached 'else' hidden + + syn foldlevel minimum + syn sync fromstart + let a = map(range(11,14), 'foldclosed(v:val)') + call assert_equal([11,11,-1,-1], a) " over-attached 'else' visible + + quit! +endfunc diff --git a/src/nvim/testdir/test_tagjump.vim b/src/nvim/testdir/test_tagjump.vim index 5fd71d8bfc..6abe5b7c89 100644 --- a/src/nvim/testdir/test_tagjump.vim +++ b/src/nvim/testdir/test_tagjump.vim @@ -255,8 +255,52 @@ func Test_tagjump_etags() ta foo call assert_equal('void foo() {}', getline('.')) + " Test for including another tags file + call writefile([ + \ "\x0c", + \ "Xmain.c,64", + \ "void foo() {}\x7ffoo\x011,0", + \ "\x0c", + \ "Xnonexisting,include", + \ "\x0c", + \ "Xtags2,include" + \ ], 'Xtags') + call writefile([ + \ "\x0c", + \ "Xmain.c,64", + \ "int main(int argc, char **argv)\x7fmain\x012,14", + \ ], 'Xtags2') + tag main + call assert_equal(2, line('.')) + + " corrupted tag line + call writefile([ + \ "\x0c", + \ "Xmain.c,8", + \ "int main" + \ ], 'Xtags', 'b') + call assert_fails('tag foo', 'E426:') + + " invalid line number + call writefile([ + \ "\x0c", + \ "Xmain.c,64", + \ "void foo() {}\x7ffoo\x0abc,0", + \ ], 'Xtags') + call assert_fails('tag foo', 'E426:') + + " invalid tag name + call writefile([ + \ "\x0c", + \ "Xmain.c,64", + \ ";;;;\x7f1,0", + \ ], 'Xtags') + call assert_fails('tag foo', 'E426:') + call delete('Xtags') + call delete('Xtags2') call delete('Xmain.c') + set tags& bwipe! endfunc @@ -531,4 +575,29 @@ func Test_tagline() set tags& endfunc +" Test for the 'taglength' option +func Test_tag_length() + set tags=Xtags + call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//", + \ "tame\tXfile1\t1;", + \ "tape\tXfile2\t1;"], 'Xtags') + call writefile(['tame'], 'Xfile1') + call writefile(['tape'], 'Xfile2') + + " Jumping to the tag 'tape', should instead jump to 'tame' + new + set taglength=2 + tag tape + call assert_equal('Xfile1', @%) + " Tag search should jump to the right tag + enew + tag /^tape$ + call assert_equal('Xfile2', @%) + + call delete('Xtags') + call delete('Xfile1') + call delete('Xfile2') + set tags& taglength& +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_textobjects.vim b/src/nvim/testdir/test_textobjects.vim index b20c4df311..7863317eb0 100644 --- a/src/nvim/testdir/test_textobjects.vim +++ b/src/nvim/testdir/test_textobjects.vim @@ -290,5 +290,16 @@ func! Test_sentence_with_cursor_on_delimiter() normal! 17|yas call assert_equal("A '([sentence.])' ", @") + " don't get stuck on a quote at the start of a sentence + %delete _ + call setline(1, ['A sentence.', '"A sentence"?', 'A sentence!']) + normal gg)) + call assert_equal(3, getcurpos()[1]) + + %delete _ + call setline(1, ['A sentence.', "'A sentence'?", 'A sentence!']) + normal gg)) + call assert_equal(3, getcurpos()[1]) + %delete _ endfunc |
