diff options
Diffstat (limited to 'src')
45 files changed, 851 insertions, 424 deletions
diff --git a/src/clint.py b/src/clint.py index 07733d211e..0c9f55c71e 100755 --- a/src/clint.py +++ b/src/clint.py @@ -2516,6 +2516,10 @@ def CheckSpacing(filename, clean_lines, linenum, nesting_state, error): cast_line = re.sub(r'^# *define +\w+\([^)]*\)', '', line) match = Search(r'(?<!\bkvec_t)' + r'(?<!\bkvec_withinit_t)' + r'(?<!\bklist_t)' + r'(?<!\bkliter_t)' + r'(?<!\bkhash_t)' r'\((?:const )?(?:struct )?[a-zA-Z_]\w*(?: *\*(?:const)?)*\)' r' +' r'-?(?:\*+|&)?(?:\w+|\+\+|--|\()', cast_line) @@ -2560,22 +2564,43 @@ def CheckBraces(filename, clean_lines, linenum, error): line = clean_lines.elided[linenum] # get rid of comments and strings - if not (filename.endswith('.c') or filename.endswith('.h')): - if Match(r'\s*{\s*$', line): - # We allow an open brace to start a line in the case where someone - # is using braces in a block to explicitly create a new scope, which - # is commonly used to control the lifetime of stack-allocated - # variables. Braces are also used for brace initializers inside - # function calls. We don't detect this perfectly: we just don't - # complain if the last non-whitespace character on the previous - # non-blank line is ',', ';', ':', '(', '{', or '}', or if the - # previous line starts a preprocessor block. - prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] - if (not Search(r'[,;:}{(]\s*$', prevline) and - not Match(r'\s*#', prevline)): - error(filename, linenum, 'whitespace/braces', 4, - '{ should almost always be at the end' - ' of the previous line') + if Match(r'\s+{\s*$', line): + # We allow an open brace to start a line in the case where someone + # is using braces in a block to explicitly create a new scope, which + # is commonly used to control the lifetime of stack-allocated + # variables. Braces are also used for brace initializers inside + # function calls. We don't detect this perfectly: we just don't + # complain if the last non-whitespace character on the previous + # non-blank line is ',', ';', ':', '(', '{', or '}', or if the + # previous line starts a preprocessor block. + prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] + if (not Search(r'[,;:}{(]\s*$', prevline) and + not Match(r'\s*#', prevline)): + error(filename, linenum, 'whitespace/braces', 4, + '{ should almost always be at the end' + ' of the previous line') + + # Brace must appear after function signature, but on the *next* line + if Match(r'^(?:\w+(?: ?\*+)? )+\w+\(', line): + pos = line.find('(') + (endline, end_linenum, endpos) = CloseExpression( + clean_lines, linenum, pos) + if endline.endswith('{'): + error(filename, end_linenum, 'readability/braces', 5, + 'Brace starting function body must be placed on its own line') + else: + func_start_linenum = end_linenum + 1 + while not clean_lines.lines[func_start_linenum] == '{': + if not Match(r'^(?:\s*\b(?:FUNC_ATTR|REAL_FATTR)_\w+\b(?:\(\d+(, \d+)*\))?)+$', + clean_lines.lines[func_start_linenum]): + if clean_lines.lines[func_start_linenum].endswith('{'): + error(filename, func_start_linenum, + 'readability/braces', 5, + 'Brace starting function body must be placed ' + 'after the function signature') + break + else: + func_start_linenum += 1 # An else clause should be on the same line as the preceding closing brace. # If there is no preceding closing brace, there should be one. @@ -3409,8 +3434,9 @@ def ProcessFile(filename, vlevel, extra_check_functions=[]): # When reading from stdin, the extension is unknown, so no cpplint tests # should rely on the extension. if filename != '-' and file_extension not in _valid_extensions: - sys.stderr.write('Ignoring %s; not a valid file name ' - '(%s)\n' % (filename, ', '.join(_valid_extensions))) + sys.stderr.write('Ignoring {}; only linting {} files\n'.format( + filename, + ', '.join('.{}'.format(ext) for ext in _valid_extensions))) else: ProcessFileData(filename, file_extension, lines, Error, extra_check_functions) diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 701a1cbf2b..7daa4d7207 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -387,6 +387,8 @@ static inline void typval_encode_list_start(EncodedData *const edata, #define TYPVAL_ENCODE_CONV_LIST_START(tv, len) \ typval_encode_list_start(edata, (size_t)(len)) +#define TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, mpsv) + static inline void typval_encode_between_list_items(EncodedData *const edata) FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL { @@ -427,6 +429,8 @@ static inline void typval_encode_dict_start(EncodedData *const edata, #define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) \ typval_encode_dict_start(edata, (size_t)(len)) +#define TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, dict, mpsv) + #define TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(label, kv_pair) static inline void typval_encode_after_key(EncodedData *const edata) @@ -499,11 +503,13 @@ static inline void typval_encode_dict_end(EncodedData *const edata) #undef TYPVAL_ENCODE_CONV_FUNC_END #undef TYPVAL_ENCODE_CONV_EMPTY_LIST #undef TYPVAL_ENCODE_CONV_LIST_START +#undef TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START #undef TYPVAL_ENCODE_CONV_EMPTY_DICT #undef TYPVAL_ENCODE_CONV_NIL #undef TYPVAL_ENCODE_CONV_BOOL #undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER #undef TYPVAL_ENCODE_CONV_DICT_START +#undef TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START #undef TYPVAL_ENCODE_CONV_DICT_END #undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY #undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index fa41f0f382..600cf575fc 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -189,14 +189,18 @@ open_buffer ( curwin->w_cursor.lnum = 1; curwin->w_cursor.col = 0; - /* Set or reset 'modified' before executing autocommands, so that - * it can be changed there. */ - if (!readonlymode && !bufempty()) + // Set or reset 'modified' before executing autocommands, so that + // it can be changed there. + if (!readonlymode && !bufempty()) { changed(); - else if (retval != FAIL) - unchanged(curbuf, FALSE); - apply_autocmds_retval(EVENT_STDINREADPOST, NULL, NULL, FALSE, - curbuf, &retval); + } else if (retval == OK) { + unchanged(curbuf, false); + } + + if (retval == OK) { + apply_autocmds_retval(EVENT_STDINREADPOST, NULL, NULL, false, + curbuf, &retval); + } } } @@ -206,22 +210,21 @@ open_buffer ( parse_cino(curbuf); } - /* - * Set/reset the Changed flag first, autocmds may change the buffer. - * Apply the automatic commands, before processing the modelines. - * So the modelines have priority over auto commands. - */ - /* When reading stdin, the buffer contents always needs writing, so set - * the changed flag. Unless in readonly mode: "ls | nvim -R -". - * When interrupted and 'cpoptions' contains 'i' set changed flag. */ + // Set/reset the Changed flag first, autocmds may change the buffer. + // Apply the automatic commands, before processing the modelines. + // So the modelines have priority over auto commands. + + // When reading stdin, the buffer contents always needs writing, so set + // the changed flag. Unless in readonly mode: "ls | nvim -R -". + // When interrupted and 'cpoptions' contains 'i' set changed flag. if ((got_int && vim_strchr(p_cpo, CPO_INTMOD) != NULL) - || modified_was_set /* ":set modified" used in autocmd */ - || (aborting() && vim_strchr(p_cpo, CPO_INTMOD) != NULL) - ) + || modified_was_set // ":set modified" used in autocmd + || (aborting() && vim_strchr(p_cpo, CPO_INTMOD) != NULL)) { changed(); - else if (retval != FAIL && !read_stdin) - unchanged(curbuf, FALSE); - save_file_ff(curbuf); /* keep this fileformat */ + } else if (retval == OK && !read_stdin) { + unchanged(curbuf, false); + } + save_file_ff(curbuf); // keep this fileformat /* require "!" to overwrite the file, because it wasn't read completely */ if (aborting()) @@ -3442,7 +3445,7 @@ int build_stl_str_hl( case STL_KEYMAP: fillable = false; - if (get_keymap_str(wp, tmp, TMPLEN)) + if (get_keymap_str(wp, (char_u *)"<%s>", tmp, TMPLEN)) str = tmp; break; case STL_PAGENUM: diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 4501c2e0a6..6b14d21da7 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -6410,8 +6410,8 @@ static void dict_free_contents(dict_T *d) { while (!QUEUE_EMPTY(&d->watchers)) { QUEUE *w = QUEUE_HEAD(&d->watchers); DictWatcher *watcher = dictwatcher_node_data(w); - dictwatcher_free(watcher); QUEUE_REMOVE(w); + dictwatcher_free(watcher); } hash_clear(&d->dv_hashtab); @@ -6445,7 +6445,7 @@ void dict_free(dict_T *d) { */ dictitem_T *dictitem_alloc(char_u *key) FUNC_ATTR_NONNULL_RET { - dictitem_T *di = xmalloc(sizeof(dictitem_T) + STRLEN(key)); + dictitem_T *di = xmalloc(offsetof(dictitem_T, di_key) + STRLEN(key) + 1); #ifndef __clang_analyzer__ STRCPY(di->di_key, key); #endif @@ -6726,6 +6726,7 @@ static bool get_dict_callback(dict_T *d, char *key, Callback *result) /// Get a string item from a dictionary. /// /// @param save whether memory should be allocated for the return value +/// when false a shared buffer is used, can only be used once! /// /// @return the entry or NULL if the entry doesn't exist. char_u *get_dict_string(dict_T *d, char *key, bool save) @@ -8828,34 +8829,73 @@ static void f_executable(typval_T *argvars, typval_T *rettv, FunPtr fptr) || (gettail_dir(name) != name && os_can_exe(name, NULL, false)); } +static char_u * get_list_line(int c, void *cookie, int indent) +{ + listitem_T **p = (listitem_T **)cookie; + listitem_T *item = *p; + char_u buf[NUMBUFLEN]; + char_u *s; + + if (item == NULL) { + return NULL; + } + s = get_tv_string_buf_chk(&item->li_tv, buf); + *p = item->li_next; + return s == NULL ? NULL : vim_strsave(s); +} + // "execute(command)" function static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int save_msg_silent = msg_silent; + int save_emsg_silent = emsg_silent; + bool save_emsg_noredir = emsg_noredir; garray_T *save_capture_ga = capture_ga; if (check_secure()) { return; } + if (argvars[1].v_type != VAR_UNKNOWN) { + char_u buf[NUMBUFLEN]; + char_u *s = get_tv_string_buf_chk(&argvars[1], buf); + + if (s == NULL) { + return; + } + if (STRNCMP(s, "silent", 6) == 0) { + msg_silent++; + } + if (STRCMP(s, "silent!") == 0) { + emsg_silent = true; + emsg_noredir = true; + } + } else { + msg_silent++; + } + garray_T capture_local; + ga_init(&capture_local, (int)sizeof(char), 80); capture_ga = &capture_local; - ga_init(capture_ga, (int)sizeof(char), 80); - msg_silent++; if (argvars[0].v_type != VAR_LIST) { do_cmdline_cmd((char *)get_tv_string(&argvars[0])); } else if (argvars[0].vval.v_list != NULL) { - for (listitem_T *li = argvars[0].vval.v_list->lv_first; - li != NULL; li = li->li_next) { - do_cmdline_cmd((char *)get_tv_string(&li->li_tv)); - } + list_T *list = argvars[0].vval.v_list; + list->lv_refcount++; + listitem_T *item = list->lv_first; + do_cmdline(NULL, get_list_line, (void *)&item, + DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT|DOCMD_KEYTYPED); + list->lv_refcount--; } msg_silent = save_msg_silent; + emsg_silent = save_emsg_silent; + emsg_noredir = save_emsg_noredir; ga_append(capture_ga, NUL); rettv->v_type = VAR_STRING; - rettv->vval.v_string = capture_ga->ga_data; + rettv->vval.v_string = vim_strsave(capture_ga->ga_data); + ga_clear(capture_ga); capture_ga = save_capture_ga; } @@ -9534,24 +9574,29 @@ static void f_foldlevel(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_foldtext(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - linenr_T lnum; + linenr_T foldstart; + linenr_T foldend; + char_u *dashes; + linenr_T lnum; char_u *s; char_u *r; - int len; + int len; char *txt; + long count; rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; - if ((linenr_T)vimvars[VV_FOLDSTART].vv_nr > 0 - && (linenr_T)vimvars[VV_FOLDEND].vv_nr - <= curbuf->b_ml.ml_line_count - && vimvars[VV_FOLDDASHES].vv_str != NULL) { + + foldstart = (linenr_T)get_vim_var_nr(VV_FOLDSTART); + foldend = (linenr_T)get_vim_var_nr(VV_FOLDEND); + dashes = get_vim_var_str(VV_FOLDDASHES); + if (foldstart > 0 && foldend <= curbuf->b_ml.ml_line_count + && dashes != NULL) { /* Find first non-empty line in the fold. */ - lnum = (linenr_T)vimvars[VV_FOLDSTART].vv_nr; - while (lnum < (linenr_T)vimvars[VV_FOLDEND].vv_nr) { - if (!linewhite(lnum)) + for (lnum = foldstart; lnum < foldend; ++lnum) { + if (!linewhite(lnum)) { break; - ++lnum; + } } /* Find interesting text in this line. */ @@ -9559,21 +9604,19 @@ static void f_foldtext(typval_T *argvars, typval_T *rettv, FunPtr fptr) /* skip C comment-start */ if (s[0] == '/' && (s[1] == '*' || s[1] == '/')) { s = skipwhite(s + 2); - if (*skipwhite(s) == NUL - && lnum + 1 < (linenr_T)vimvars[VV_FOLDEND].vv_nr) { + if (*skipwhite(s) == NUL && lnum + 1 < foldend) { s = skipwhite(ml_get(lnum + 1)); if (*s == '*') s = skipwhite(s + 1); } } + count = (long)(foldend - foldstart + 1); txt = _("+-%s%3ld lines: "); r = xmalloc(STRLEN(txt) - + STRLEN(vimvars[VV_FOLDDASHES].vv_str) // for %s - + 20 // for %3ld - + STRLEN(s)); // concatenated - sprintf((char *)r, txt, vimvars[VV_FOLDDASHES].vv_str, - (long)((linenr_T)vimvars[VV_FOLDEND].vv_nr - - (linenr_T)vimvars[VV_FOLDSTART].vv_nr + 1)); + + STRLEN(dashes) // for %s + + 20 // for %3ld + + STRLEN(s)); // concatenated + sprintf((char *)r, txt, dashes, count); len = (int)STRLEN(r); STRCAT(r, s); /* remove 'foldmarker' and 'commentstring' */ @@ -15406,12 +15449,11 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } - char_u *group = get_dict_string(d, "group", false); + char_u *group = get_dict_string(d, "group", true); int priority = get_dict_number(d, "priority"); int id = get_dict_number(d, "id"); char_u *conceal = dict_find(d, (char_u *)"conceal", -1) != NULL - ? get_dict_string(d, "conceal", - false) + ? get_dict_string(d, "conceal", true) : NULL; if (i == 0) { match_add(curwin, group, @@ -15422,6 +15464,8 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) list_unref(s); s = NULL; } + xfree(group); + xfree(conceal); li = li->li_next; } rettv->vval.v_number = 0; @@ -16955,8 +16999,12 @@ static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, } // get shell command to execute - char **argv = tv_to_argv(&argvars[0], NULL, NULL); + bool executable = true; + char **argv = tv_to_argv(&argvars[0], NULL, &executable); if (!argv) { + if (!executable) { + set_vim_var_nr(VV_SHELL_ERROR, (long)-1); + } xfree(input); return; // Already did emsg. } @@ -19147,23 +19195,25 @@ static inline void _nothing_conv_func_end(typval_T *const tv, const int copyID) } \ } while (0) -static inline int _nothing_conv_list_start(typval_T *const tv) +static inline int _nothing_conv_real_list_after_start( + typval_T *const tv, MPConvStackVal *const mpsv) FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_WARN_UNUSED_RESULT { - if (tv == NULL) { - return NOTDONE; - } + assert(tv != NULL); tv->v_lock = VAR_UNLOCKED; if (tv->vval.v_list->lv_refcount > 1) { tv->vval.v_list->lv_refcount--; tv->vval.v_list = NULL; + mpsv->data.l.li = NULL; return OK; } return NOTDONE; } -#define TYPVAL_ENCODE_CONV_LIST_START(tv, len) \ +#define TYPVAL_ENCODE_CONV_LIST_START(tv, len) + +#define TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, mpsv) \ do { \ - if (_nothing_conv_list_start(tv) != NOTDONE) { \ + if (_nothing_conv_real_list_after_start(tv, &mpsv) != NOTDONE) { \ goto typval_encode_stop_converting_one_item; \ } \ } while (0) @@ -19183,9 +19233,9 @@ static inline void _nothing_conv_list_end(typval_T *const tv) } #define TYPVAL_ENCODE_CONV_LIST_END(tv) _nothing_conv_list_end(tv) -static inline int _nothing_conv_dict_start(typval_T *const tv, - dict_T **const dictp, - const void *const nodictvar) +static inline int _nothing_conv_real_dict_after_start( + typval_T *const tv, dict_T **const dictp, const void *const nodictvar, + MPConvStackVal *const mpsv) FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_WARN_UNUSED_RESULT { if (tv != NULL) { @@ -19194,15 +19244,18 @@ static inline int _nothing_conv_dict_start(typval_T *const tv, if ((const void *)dictp != nodictvar && (*dictp)->dv_refcount > 1) { (*dictp)->dv_refcount--; *dictp = NULL; + mpsv->data.d.todo = 0; return OK; } return NOTDONE; } -#define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) \ +#define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) + +#define TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, dict, mpsv) \ do { \ - if (_nothing_conv_dict_start(tv, (dict_T **)&dict, \ - (void *)&TYPVAL_ENCODE_NODICT_VAR) \ - != NOTDONE) { \ + if (_nothing_conv_real_dict_after_start( \ + tv, (dict_T **)&dict, (void *)&TYPVAL_ENCODE_NODICT_VAR, \ + &mpsv) != NOTDONE) { \ goto typval_encode_stop_converting_one_item; \ } \ } while (0) @@ -19253,9 +19306,11 @@ static inline void _nothing_conv_dict_end(typval_T *const tv, #undef TYPVAL_ENCODE_CONV_EMPTY_LIST #undef TYPVAL_ENCODE_CONV_EMPTY_DICT #undef TYPVAL_ENCODE_CONV_LIST_START +#undef TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START #undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS #undef TYPVAL_ENCODE_CONV_LIST_END #undef TYPVAL_ENCODE_CONV_DICT_START +#undef TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START #undef TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK #undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY #undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS @@ -21658,9 +21713,12 @@ void func_unref(char_u *name) fp = find_func(name); if (fp == NULL) { #ifdef EXITFREE - if (!entered_free_all_mem) // NOLINT(readability/braces) -#endif + if (!entered_free_all_mem) { EMSG2(_(e_intern2), "func_unref()"); + } +#else + EMSG2(_(e_intern2), "func_unref()"); +#endif } else { user_func_unref(fp); } @@ -23130,11 +23188,10 @@ static void on_job_output(Stream *stream, TerminalJobData *data, RBuffer *buf, terminal_receive(data->term, ptr, count); } + rbuffer_consumed(buf, count); if (callback->type != kCallbackNone) { process_job_event(data, callback, type, ptr, count, 0); } - - rbuffer_consumed(buf, count); } static void eval_job_process_exit_cb(Process *proc, int status, void *d) diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 5fb99fecc6..e0b72feb19 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -79,7 +79,7 @@ return { eval={args=1}, eventhandler={}, executable={args=1}, - execute={args=1}, + execute={args={1, 2}}, exepath={args=1}, exists={args=1}, exp={args=1, func="float_op_wrapper", data="&exp"}, diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c index 071fbc3923..ee66b7cf09 100644 --- a/src/nvim/eval/encode.c +++ b/src/nvim/eval/encode.c @@ -369,6 +369,8 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, #define TYPVAL_ENCODE_CONV_LIST_START(tv, len) \ ga_append(gap, '[') +#define TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, mpsv) + #define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \ ga_concat(gap, "{}") @@ -383,6 +385,8 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, #define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) \ ga_append(gap, '{') +#define TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, dict, mpsv) + #define TYPVAL_ENCODE_CONV_DICT_END(tv, dict) \ ga_append(gap, '}') @@ -789,11 +793,13 @@ bool encode_check_json_key(const typval_T *const tv) #undef TYPVAL_ENCODE_CONV_FUNC_END #undef TYPVAL_ENCODE_CONV_EMPTY_LIST #undef TYPVAL_ENCODE_CONV_LIST_START +#undef TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START #undef TYPVAL_ENCODE_CONV_EMPTY_DICT #undef TYPVAL_ENCODE_CONV_NIL #undef TYPVAL_ENCODE_CONV_BOOL #undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER #undef TYPVAL_ENCODE_CONV_DICT_START +#undef TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START #undef TYPVAL_ENCODE_CONV_DICT_END #undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY #undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS @@ -933,6 +939,8 @@ char *encode_tv2json(typval_T *tv, size_t *len) #define TYPVAL_ENCODE_CONV_LIST_START(tv, len) \ msgpack_pack_array(packer, (size_t)(len)) +#define TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, mpsv) + #define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \ msgpack_pack_map(packer, 0) @@ -954,6 +962,8 @@ char *encode_tv2json(typval_T *tv, size_t *len) #define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) \ msgpack_pack_map(packer, (size_t)(len)) +#define TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, dict, mpsv) + #define TYPVAL_ENCODE_CONV_DICT_END(tv, dict) #define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(tv, dict) @@ -994,11 +1004,13 @@ char *encode_tv2json(typval_T *tv, size_t *len) #undef TYPVAL_ENCODE_CONV_FUNC_END #undef TYPVAL_ENCODE_CONV_EMPTY_LIST #undef TYPVAL_ENCODE_CONV_LIST_START +#undef TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START #undef TYPVAL_ENCODE_CONV_EMPTY_DICT #undef TYPVAL_ENCODE_CONV_NIL #undef TYPVAL_ENCODE_CONV_BOOL #undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER #undef TYPVAL_ENCODE_CONV_DICT_START +#undef TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START #undef TYPVAL_ENCODE_CONV_DICT_END #undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY #undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS diff --git a/src/nvim/eval/typval_encode.c.h b/src/nvim/eval/typval_encode.c.h index 3e1170b8fa..365eb2dd77 100644 --- a/src/nvim/eval/typval_encode.c.h +++ b/src/nvim/eval/typval_encode.c.h @@ -129,6 +129,16 @@ /// point to a special dictionary. /// @param len List length. Is an expression which evaluates to an integer. +/// @def TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START +/// @brief Macros used after pushing list onto the stack +/// +/// Only used for real list_T* lists, not for special dictionaries or partial +/// arguments. +/// +/// @param tv Pointer to typval where value is stored. May be NULL. May +/// point to a special dictionary. +/// @param mpsv Pushed MPConvStackVal value. + /// @def TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS /// @brief Macros used after finishing converting non-last list item /// @@ -142,6 +152,9 @@ /// @def TYPVAL_ENCODE_CONV_DICT_START /// @brief Macros used before starting to convert non-empty dictionary /// +/// Only used for real dict_T* dictionaries, not for special dictionaries. Also +/// used for partial self dictionary. +/// /// @param tv Pointer to typval where dictionary is stored. May be NULL. May /// point to a special dictionary. /// @param dict Converted dictionary, lvalue or #TYPVAL_ENCODE_NODICT_VAR @@ -149,6 +162,14 @@ /// @param len Dictionary length. Is an expression which evaluates to an /// integer. +/// @def TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START +/// @brief Macros used after pushing dictionary onto the stack +/// +/// @param tv Pointer to typval where dictionary is stored. May be NULL. +/// May not point to a special dictionary. +/// @param dict Converted dictionary, lvalue. +/// @param mpsv Pushed MPConvStackVal value. + /// @def TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK /// @brief Macros used to check special dictionary key /// @@ -354,6 +375,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( }, }, })); + TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, _mp_last(*mpstack)); break; } case VAR_SPECIAL: { @@ -564,6 +586,8 @@ _convert_one_value_regular_dict: }, }, })); + TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, tv->vval.v_dict, + _mp_last(*mpstack)); break; } case VAR_UNKNOWN: { @@ -732,6 +756,8 @@ typval_encode_stop_converting_one_item: }, }, })); + TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(NULL, pt->pt_dict, + _mp_last(mpstack)); } else { TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, -1); } diff --git a/src/nvim/eval/typval_encode.h b/src/nvim/eval/typval_encode.h index 6517efa961..ba325b8f55 100644 --- a/src/nvim/eval/typval_encode.h +++ b/src/nvim/eval/typval_encode.h @@ -108,37 +108,38 @@ static inline size_t tv_strlen(const typval_T *const tv) } \ } while (0) -#define _TYPVAL_ENCODE_CHECK_SELF_REFERENCE_INNER_2(name) \ - _typval_encode_##name##_check_self_reference -#define _TYPVAL_ENCODE_CHECK_SELF_REFERENCE_INNER(name) \ - _TYPVAL_ENCODE_CHECK_SELF_REFERENCE_INNER_2(name) +#define _TYPVAL_ENCODE_FUNC_NAME_INNER_2(pref, name, suf) \ + pref##name##suf +#define _TYPVAL_ENCODE_FUNC_NAME_INNER(pref, name, suf) \ + _TYPVAL_ENCODE_FUNC_NAME_INNER_2(pref, name, suf) + +/// Construct function name, possibly using macros +/// +/// Is used to expand macros that may appear in arguments. +/// +/// @note Expands all arguments, even if only one is needed. +/// +/// @param[in] pref Prefix. +/// @param[in] suf Suffix. +/// +/// @return Concat: pref + #TYPVAL_ENCODE_NAME + suf. +#define _TYPVAL_ENCODE_FUNC_NAME(pref, suf) \ + _TYPVAL_ENCODE_FUNC_NAME_INNER(pref, TYPVAL_ENCODE_NAME, suf) /// Self reference checker function name #define _TYPVAL_ENCODE_CHECK_SELF_REFERENCE \ - _TYPVAL_ENCODE_CHECK_SELF_REFERENCE_INNER(TYPVAL_ENCODE_NAME) - -#define _TYPVAL_ENCODE_ENCODE_INNER_2(name) encode_vim_to_##name -#define _TYPVAL_ENCODE_ENCODE_INNER(name) _TYPVAL_ENCODE_ENCODE_INNER_2(name) + _TYPVAL_ENCODE_FUNC_NAME(_typval_encode_, _check_self_reference) /// Entry point function name -#define _TYPVAL_ENCODE_ENCODE _TYPVAL_ENCODE_ENCODE_INNER(TYPVAL_ENCODE_NAME) - -#define _TYPVAL_ENCODE_CONVERT_ONE_VALUE_INNER_2(name) \ - _typval_encode_##name##_convert_one_value -#define _TYPVAL_ENCODE_CONVERT_ONE_VALUE_INNER(name) \ - _TYPVAL_ENCODE_CONVERT_ONE_VALUE_INNER_2(name) +#define _TYPVAL_ENCODE_ENCODE \ + _TYPVAL_ENCODE_FUNC_NAME(encode_vim_to_, ) /// Name of the …convert_one_value function #define _TYPVAL_ENCODE_CONVERT_ONE_VALUE \ - _TYPVAL_ENCODE_CONVERT_ONE_VALUE_INNER(TYPVAL_ENCODE_NAME) - -#define _TYPVAL_ENCODE_NODICT_VAR_INNER_2(name) \ - _typval_encode_##name##_nodict_var -#define _TYPVAL_ENCODE_NODICT_VAR_INNER(name) \ - _TYPVAL_ENCODE_NODICT_VAR_INNER_2(name) + _TYPVAL_ENCODE_FUNC_NAME(_typval_encode_, _convert_one_value) /// Name of the dummy const dict_T *const variable #define TYPVAL_ENCODE_NODICT_VAR \ - _TYPVAL_ENCODE_NODICT_VAR_INNER(TYPVAL_ENCODE_NAME) + _TYPVAL_ENCODE_FUNC_NAME(_typval_encode_, _nodict_var) #endif // NVIM_EVAL_TYPVAL_ENCODE_H diff --git a/src/nvim/event/libuv_process.c b/src/nvim/event/libuv_process.c index a68badcc8f..907187aa17 100644 --- a/src/nvim/event/libuv_process.c +++ b/src/nvim/event/libuv_process.c @@ -19,8 +19,7 @@ bool libuv_process_spawn(LibuvProcess *uvproc) Process *proc = (Process *)uvproc; uvproc->uvopts.file = proc->argv[0]; uvproc->uvopts.args = proc->argv; - uvproc->uvopts.flags = UV_PROCESS_WINDOWS_HIDE - | UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS; + uvproc->uvopts.flags = UV_PROCESS_WINDOWS_HIDE; if (proc->detach) { uvproc->uvopts.flags |= UV_PROCESS_DETACHED; } diff --git a/src/nvim/event/process.c b/src/nvim/event/process.c index 39dd5fd55a..dc7886469b 100644 --- a/src/nvim/event/process.c +++ b/src/nvim/event/process.c @@ -170,8 +170,9 @@ int process_wait(Process *proc, int ms, MultiQueue *events) int status = -1; bool interrupted = false; if (!proc->refcount) { + status = proc->status; LOOP_PROCESS_EVENTS(proc->loop, proc->events, 0); - return proc->status; + return status; } if (!events) { diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index b1a17e8c44..69eed33736 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -1193,8 +1193,8 @@ static void do_filter( if (do_out) { if (otmp != NULL) { - if (readfile(otmp, NULL, line2, (linenr_T)0, (linenr_T)MAXLNUM, - eap, READ_FILTER) == FAIL) { + if (readfile(otmp, NULL, line2, (linenr_T)0, (linenr_T)MAXLNUM, eap, + READ_FILTER) != OK) { if (!aborting()) { msg_putchar('\n'); EMSG2(_(e_notread), otmp); @@ -6093,7 +6093,6 @@ void ex_substitute(exarg_T *eap) int save_w_p_cul = curwin->w_p_cul; int save_w_p_cuc = curwin->w_p_cuc; - emsg_off++; // No error messages during command preview. curbuf->b_p_ul = LONG_MAX; // make sure we can undo all changes curwin->w_p_cul = false; // Disable 'cursorline' curwin->w_p_cuc = false; // Disable 'cursorcolumn' @@ -6120,6 +6119,5 @@ void ex_substitute(exarg_T *eap) restore_search_patterns(); win_size_restore(&save_view); ga_clear(&save_view); - emsg_off--; unblock_autocmds(); } diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua index b998b81284..88095602ba 100644 --- a/src/nvim/ex_cmds.lua +++ b/src/nvim/ex_cmds.lua @@ -480,6 +480,12 @@ return { func='ex_close', }, { + command='clearjumps', + flags=bit.bor(TRLBAR, CMDWIN), + addr_type=ADDR_LINES, + func='ex_clearjumps', + }, + { command='cmap', flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), addr_type=ADDR_LINES, diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index eccece7ac7..5ff79bcfef 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -6910,9 +6910,10 @@ static void ex_read(exarg_T *eap) eap->line2, (linenr_T)0, (linenr_T)MAXLNUM, eap, 0); } - if (i == FAIL) { - if (!aborting()) + if (i != OK) { + if (!aborting()) { EMSG2(_(e_notopen), eap->arg); + } } else { if (empty && exmode_active) { /* Delete the empty line that remains. Historically ex does @@ -9674,9 +9675,20 @@ bool cmd_can_preview(char_u *cmd) if (*ea.cmd == '*') { ea.cmd = skipwhite(ea.cmd + 1); } - find_command(&ea, NULL); + char_u *end = find_command(&ea, NULL); + + switch (ea.cmdidx) { + case CMD_substitute: + case CMD_smagic: + case CMD_snomagic: + // Only preview once the pattern delimiter has been typed + if (*end && !ASCII_ISALNUM(*end)) { + return true; + } + break; + default: + break; + } - return ea.cmdidx == CMD_substitute - || ea.cmdidx == CMD_smagic - || ea.cmdidx == CMD_snomagic; + return false; } diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 7c725179dd..dba7a73814 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -1607,7 +1607,9 @@ static int command_line_changed(CommandLineState *s) // - Update the screen while the effects are in place. // - Immediately undo the effects. State |= CMDPREVIEW; + emsg_silent++; // Block error reporting as the command may be incomplete do_cmdline(ccline.cmdbuff, NULL, NULL, DOCMD_KEEPLINE|DOCMD_NOWAIT); + emsg_silent--; // Unblock error reporting // Restore the window "view". curwin->w_cursor = s->old_cursor; diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index e10f7fd2a2..e734cde905 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -51,6 +51,7 @@ #include "nvim/window.h" #include "nvim/shada.h" #include "nvim/os/os.h" +#include "nvim/os/os_defs.h" #include "nvim/os/time.h" #include "nvim/os/input.h" @@ -248,7 +249,7 @@ void filemess(buf_T *buf, char_u *name, char_u *s, int attr) * READ_DUMMY read into a dummy buffer (to check if file contents changed) * READ_KEEP_UNDO don't clear undo info or read it from a file * - * return FAIL for failure, OK otherwise + * return FAIL for failure, NOTDONE for directory (failure), or OK */ int readfile ( @@ -257,7 +258,7 @@ readfile ( linenr_T from, linenr_T lines_to_skip, linenr_T lines_to_read, - exarg_T *eap, /* can be NULL! */ + exarg_T *eap, // can be NULL! int flags ) { @@ -443,13 +444,14 @@ readfile ( // ... or a character special file named /dev/fd/<n> # endif ) { - if (S_ISDIR(perm)) + if (S_ISDIR(perm)) { filemess(curbuf, fname, (char_u *)_("is a directory"), 0); - else + } else { filemess(curbuf, fname, (char_u *)_("is not a file"), 0); + } msg_end(); msg_scroll = msg_save; - return FAIL; + return S_ISDIR(perm) ? NOTDONE : FAIL; } #endif } @@ -5103,13 +5105,13 @@ void buf_reload(buf_T *buf, int orig_mode) } if (saved == OK) { - curbuf->b_flags |= BF_CHECK_RO; /* check for RO again */ - keep_filetype = TRUE; /* don't detect 'filetype' */ - if (readfile(buf->b_ffname, buf->b_fname, (linenr_T)0, - (linenr_T)0, - (linenr_T)MAXLNUM, &ea, flags) == FAIL) { - if (!aborting()) + curbuf->b_flags |= BF_CHECK_RO; // check for RO again + keep_filetype = true; // don't detect 'filetype' + if (readfile(buf->b_ffname, buf->b_fname, (linenr_T)0, (linenr_T)0, + (linenr_T)MAXLNUM, &ea, flags) != OK) { + if (!aborting()) { EMSG2(_("E321: Could not reload \"%s\""), buf->b_fname); + } if (savebuf != NULL && buf_valid(savebuf) && buf == curbuf) { /* Put the text back from the save buffer. First * delete any lines that readfile() added. */ @@ -5224,6 +5226,10 @@ static void vim_maketempdir(void) // Try the entries in `TEMP_DIR_NAMES` to create the temp directory. char_u template[TEMP_FILE_PATH_MAXLEN]; char_u path[TEMP_FILE_PATH_MAXLEN]; + + // Make sure the umask doesn't remove the executable bit. + // "repl" has been reported to use "0177". + mode_t umask_save = umask(0077); for (size_t i = 0; i < ARRAY_SIZE(temp_dirs); i++) { // Expand environment variables, leave room for "/nvimXXXXXX/999999999" expand_env((char_u *)temp_dirs[i], template, TEMP_FILE_PATH_MAXLEN - 22); @@ -5247,6 +5253,7 @@ static void vim_maketempdir(void) os_rmdir((char *)path); } } + (void)umask(umask_save); } /// Delete "name" and everything in it, recursively. diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 872ff8d5b8..463f4fcd8d 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -409,10 +409,6 @@ EXTERN struct caller_scope { } provider_caller_scope; EXTERN int provider_call_nesting INIT(= 0); -/* Magic number used for hashitem "hi_key" value indicating a deleted item. - * Only the address is used. */ -EXTERN char_u hash_removed; - EXTERN int t_colors INIT(= 256); // int value of T_CCO @@ -636,10 +632,6 @@ EXTERN int exiting INIT(= FALSE); /* TRUE when planning to exit Vim. Might * still keep on running if there is a changed * buffer. */ -#if defined(EXITFREE) -// true when in or after free_all_mem() -EXTERN bool entered_free_all_mem INIT(= false); -#endif // volatile because it is used in signal handler deathtrap(). EXTERN volatile int full_screen INIT(= false); // TRUE when doing full-screen output @@ -877,9 +869,10 @@ EXTERN int mapped_ctrl_c INIT(= 0); // Modes where CTRL-C is mapped. EXTERN cmdmod_T cmdmod; /* Ex command modifiers */ -EXTERN int msg_silent INIT(= 0); /* don't print messages */ -EXTERN int emsg_silent INIT(= 0); /* don't print error messages */ -EXTERN int cmd_silent INIT(= FALSE); /* don't echo the command line */ +EXTERN int msg_silent INIT(= 0); // don't print messages +EXTERN int emsg_silent INIT(= 0); // don't print error messages +EXTERN bool emsg_noredir INIT(= false); // don't redirect error messages +EXTERN int cmd_silent INIT(= false); // don't echo the command line /* Values for swap_exists_action: what to do when swap file already exists */ #define SEA_NONE 0 /* don't use dialog */ diff --git a/src/nvim/hashtab.c b/src/nvim/hashtab.c index fa4077f22f..376f33e23e 100644 --- a/src/nvim/hashtab.c +++ b/src/nvim/hashtab.c @@ -36,6 +36,8 @@ # include "hashtab.c.generated.h" #endif +char hash_removed; + /// Initialize an empty hash table. void hash_init(hashtab_T *ht) { @@ -380,3 +382,13 @@ hash_T hash_hash(char_u *key) return hash; } + +/// Function to get HI_KEY_REMOVED value +/// +/// Used for testing because luajit ffi does not allow getting addresses of +/// globals. +const char_u *_hash_key_removed(void) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + return HI_KEY_REMOVED; +} diff --git a/src/nvim/hashtab.h b/src/nvim/hashtab.h index 7233d8c47c..0da2b13f2e 100644 --- a/src/nvim/hashtab.h +++ b/src/nvim/hashtab.h @@ -5,14 +5,19 @@ #include "nvim/types.h" +/// Magic number used for hashitem "hi_key" value indicating a deleted item +/// +/// Only the address is used. +extern char hash_removed; + /// Type for hash number (hash calculation result). typedef size_t hash_T; /// The address of "hash_removed" is used as a magic number /// for hi_key to indicate a removed item. -#define HI_KEY_REMOVED &hash_removed +#define HI_KEY_REMOVED ((char_u *)&hash_removed) #define HASHITEM_EMPTY(hi) ((hi)->hi_key == NULL \ - || (hi)->hi_key == &hash_removed) + || (hi)->hi_key == (char_u *)&hash_removed) /// A hastable item. /// diff --git a/src/nvim/keymap.c b/src/nvim/keymap.c index 99e94fc60f..94bbaf4239 100644 --- a/src/nvim/keymap.c +++ b/src/nvim/keymap.c @@ -573,8 +573,10 @@ int find_special_key(const char_u **srcp, const size_t src_len, int *const modp, } else { l = 1; } - if (end - bp > l && bp[l + 1] == '>') { - bp += l; // anything accepted, like <C-?> + if (end - bp > l && bp[l] != '"' && bp[l + 1] == '>') { + // Anything accepted, like <C-?>, except <C-">, because the " + // ends the string. + bp += l; } } } diff --git a/src/nvim/mark.c b/src/nvim/mark.c index 6453c41415..4e05845eb5 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -130,17 +130,23 @@ int setmark_pos(int c, pos_T *pos, int fnum) return OK; } - if (c > 'z') /* some islower() and isupper() cannot handle - characters above 127 */ + buf_T *buf = buflist_findnr(fnum); + // Can't set a mark in a non-existant buffer. + if (buf == NULL) { return FAIL; - if (islower(c)) { + } + + if (ASCII_ISLOWER(c)) { i = c - 'a'; - RESET_FMARK(curbuf->b_namedm + i, *pos, curbuf->b_fnum); + RESET_FMARK(buf->b_namedm + i, *pos, fnum); return OK; } - if (isupper(c)) { - assert(c >= 'A' && c <= 'Z'); - i = c - 'A'; + if (ASCII_ISUPPER(c) || ascii_isdigit(c)) { + if (ascii_isdigit(c)) { + i = c - '0' + NMARKS; + } else { + i = c - 'A'; + } RESET_XFMARK(namedfm + i, *pos, fnum, NULL); return OK; } @@ -798,6 +804,13 @@ void ex_jumps(exarg_T *eap) MSG_PUTS("\n>"); } +void ex_clearjumps(exarg_T *eap) +{ + free_jumplist(curwin); + curwin->w_jumplistlen = 0; + curwin->w_jumplistidx = 0; +} + /* * print the changelist */ diff --git a/src/nvim/memline.c b/src/nvim/memline.c index a9a53ebca7..b8891f6560 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -1063,11 +1063,12 @@ void ml_recover(void) if (!cannot_open) { line_count = pp->pb_pointer[idx].pe_line_count; if (readfile(curbuf->b_ffname, NULL, lnum, - pp->pb_pointer[idx].pe_old_lnum - 1, - line_count, NULL, 0) == FAIL) - cannot_open = TRUE; - else + pp->pb_pointer[idx].pe_old_lnum - 1, line_count, + NULL, 0) != OK) { + cannot_open = true; + } else { lnum += line_count; + } } if (cannot_open) { ++error; diff --git a/src/nvim/memory.c b/src/nvim/memory.c index 1884d55999..92ead873ae 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -17,16 +17,41 @@ // Force je_ prefix on jemalloc functions. # define JEMALLOC_NO_DEMANGLE # include <jemalloc/jemalloc.h> -# define malloc(size) je_malloc(size) -# define calloc(count, size) je_calloc(count, size) -# define realloc(ptr, size) je_realloc(ptr, size) -# define free(ptr) je_free(ptr) +#endif + +#ifdef UNIT_TESTING +# define malloc(size) mem_malloc(size) +# define calloc(count, size) mem_calloc(count, size) +# define realloc(ptr, size) mem_realloc(ptr, size) +# define free(ptr) mem_free(ptr) +# ifdef HAVE_JEMALLOC +MemMalloc mem_malloc = &je_malloc; +MemFree mem_free = &je_free; +MemCalloc mem_calloc = &je_calloc; +MemRealloc mem_realloc = &je_realloc; +# else +MemMalloc mem_malloc = &malloc; +MemFree mem_free = &free; +MemCalloc mem_calloc = &calloc; +MemRealloc mem_realloc = &realloc; +# endif +#else +# ifdef HAVE_JEMALLOC +# define malloc(size) je_malloc(size) +# define calloc(count, size) je_calloc(count, size) +# define realloc(ptr, size) je_realloc(ptr, size) +# define free(ptr) je_free(ptr) +# endif #endif #ifdef INCLUDE_GENERATED_DECLARATIONS # include "memory.c.generated.h" #endif +#ifdef EXITFREE +bool entered_free_all_mem = false; +#endif + /// Try to free memory. Used when trying to recover from out of memory errors. /// @see {xmalloc} void try_to_free_memory(void) @@ -353,15 +378,15 @@ char *xstpncpy(char *restrict dst, const char *restrict src, size_t maxlen) size_t xstrlcpy(char *restrict dst, const char *restrict src, size_t size) FUNC_ATTR_NONNULL_ALL { - size_t ret = strlen(src); + size_t ret = strlen(src); - if (size) { - size_t len = (ret >= size) ? size - 1 : ret; - memcpy(dst, src, len); - dst[len] = '\0'; - } + if (size) { + size_t len = (ret >= size) ? size - 1 : ret; + memcpy(dst, src, len); + dst[len] = '\0'; + } - return ret; + return ret; } /// strdup() wrapper @@ -371,6 +396,7 @@ size_t xstrlcpy(char *restrict dst, const char *restrict src, size_t size) /// @return pointer to a copy of the string char *xstrdup(const char *str) FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET + FUNC_ATTR_NONNULL_ALL { return xmemdupz(str, strlen(str)); } @@ -401,6 +427,7 @@ void *xmemrchr(const void *src, uint8_t c, size_t len) /// @return pointer to a copy of the string char *xstrndup(const char *str, size_t len) FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET + FUNC_ATTR_NONNULL_ALL { char *p = memchr(str, '\0', len); return xmemdupz(str, p ? (size_t)(p - str) : len); diff --git a/src/nvim/memory.h b/src/nvim/memory.h index 62cc78360c..250ac3e08f 100644 --- a/src/nvim/memory.h +++ b/src/nvim/memory.h @@ -1,9 +1,41 @@ #ifndef NVIM_MEMORY_H #define NVIM_MEMORY_H +#include <stdbool.h> // for bool #include <stdint.h> // for uint8_t #include <stddef.h> // for size_t -#include <time.h> // for time_t +#include <time.h> // for time_t + +/// `malloc()` function signature +typedef void *(*MemMalloc)(size_t); + +/// `free()` function signature +typedef void (*MemFree)(void *); + +/// `calloc()` function signature +typedef void *(*MemCalloc)(size_t, size_t); + +/// `realloc()` function signature +typedef void *(*MemRealloc)(void *, size_t); + +#ifdef UNIT_TESTING +/// When unit testing: pointer to the `malloc()` function, may be altered +extern MemMalloc mem_malloc; + +/// When unit testing: pointer to the `free()` function, may be altered +extern MemFree mem_free; + +/// When unit testing: pointer to the `calloc()` function, may be altered +extern MemCalloc mem_calloc; + +/// When unit testing: pointer to the `realloc()` function, may be altered +extern MemRealloc mem_realloc; +#endif + +#ifdef EXITFREE +/// Indicates that free_all_mem function was or is running +extern bool entered_free_all_mem; +#endif #ifdef INCLUDE_GENERATED_DECLARATIONS # include "memory.h.generated.h" diff --git a/src/nvim/message.c b/src/nvim/message.c index 637b89ccbe..2f8feda6ec 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -508,20 +508,22 @@ int emsg(char_u *s) * But do write it to the redirection file. */ if (emsg_silent != 0) { - msg_start(); - p = get_emsg_source(); - if (p != NULL) { - STRCAT(p, "\n"); - redir_write(p, STRLEN(p)); - xfree(p); - } - p = get_emsg_lnum(); - if (p != NULL) { - STRCAT(p, "\n"); - redir_write(p, STRLEN(p)); - xfree(p); + if (!emsg_noredir) { + msg_start(); + p = get_emsg_source(); + if (p != NULL) { + STRCAT(p, "\n"); + redir_write(p, STRLEN(p)); + xfree(p); + } + p = get_emsg_lnum(); + if (p != NULL) { + STRCAT(p, "\n"); + redir_write(p, STRLEN(p)); + xfree(p); + } + redir_write(s, STRLEN(s)); } - redir_write(s, STRLEN(s)); return true; } @@ -2508,8 +2510,7 @@ static void redir_write(char_u *str, int maxlen) int redirecting(void) { return redir_fd != NULL || *p_vfile != NUL - || redir_reg || redir_vname - ; + || redir_reg || redir_vname || capture_ga != NULL; } /* diff --git a/src/nvim/option.c b/src/nvim/option.c index 52a8b19ca4..a4e7da770e 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -213,12 +213,12 @@ typedef struct vimoption { #define P_VI_DEF 0x400U /* Use Vi default for Vim */ #define P_VIM 0x800U /* Vim option */ -/* when option changed, what to display: */ -#define P_RSTAT 0x1000U /* redraw status lines */ -#define P_RWIN 0x2000U /* redraw current window */ -#define P_RBUF 0x4000U /* redraw current buffer */ -#define P_RALL 0x6000U /* redraw all windows */ -#define P_RCLR 0x7000U /* clear and redraw all */ +// when option changed, what to display: +#define P_RSTAT 0x1000U ///< redraw status lines +#define P_RWIN 0x2000U ///< redraw current window and recompute text +#define P_RBUF 0x4000U ///< redraw current buffer and recompute text +#define P_RALL 0x6000U ///< redraw all windows +#define P_RCLR 0x7000U ///< clear and redraw all #define P_COMMA 0x8000U ///< comma separated list #define P_ONECOMMA 0x18000U ///< P_COMMA and cannot have two consecutive @@ -238,6 +238,8 @@ typedef struct vimoption { ///< when there is a redraw flag #define P_NO_DEF_EXP 0x8000000U ///< Do not expand default value. +#define P_RWINONLY 0x10000000U ///< only redraw current window + #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," \ @@ -4357,17 +4359,24 @@ static void check_redraw(uint32_t flags) bool doclear = (flags & P_RCLR) == P_RCLR; bool all = ((flags & P_RALL) == P_RALL || doclear); - if ((flags & P_RSTAT) || all) /* mark all status lines dirty */ + if ((flags & P_RSTAT) || all) { // mark all status lines dirty status_redraw_all(); + } - if ((flags & P_RBUF) || (flags & P_RWIN) || all) + if ((flags & P_RBUF) || (flags & P_RWIN) || all) { changed_window_setting(); - if (flags & P_RBUF) + } + if (flags & P_RBUF) { redraw_curbuf_later(NOT_VALID); - if (doclear) + } + if (flags & P_RWINONLY) { + redraw_later(NOT_VALID); + } + if (doclear) { redraw_all_later(CLEAR); - else if (all) + } else if (all) { redraw_all_later(NOT_VALID); + } } /// Find index for named option diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 70d1f73cf4..859658e40d 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -17,8 +17,8 @@ -- types: bool, number, string -- lists: (nil), comma, onecomma, flags, flagscomma -- scopes: global, buffer, window --- redraw options: statuslines, current_window, current_buffer, all_windows, --- everything, curswant +-- redraw options: statuslines, current_window, curent_window_only, +-- current_buffer, all_windows, everything, curswant -- default: {vi=…[, vim=…]} -- defaults: {condition=#if condition, if_true=default, if_false=default} -- #if condition: @@ -539,7 +539,7 @@ return { full_name='cursorline', abbreviation='cul', type='bool', scope={'window'}, vi_def=true, - redraw={'current_window'}, + redraw={'current_window_only'}, defaults={if_true={vi=false}} }, { diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index 83a0262545..47d541c6a7 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -560,8 +560,8 @@ const void *vim_colon_env_iter_rev(const char *const val, /// Vim's version of getenv(). /// Special handling of $HOME, $VIM and $VIMRUNTIME, allowing the user to /// override the vim runtime directory at runtime. Also does ACP to 'enc' -/// conversion for Win32. Results must be freed by the calling function. -/// @param name Name of environment variable to expand +/// conversion for Win32. Result must be freed by the caller. +/// @param name Environment variable to expand char *vim_getenv(const char *name) { const char *kos_env_path = os_getenv(name); @@ -598,6 +598,27 @@ char *vim_getenv(const char *name) if (p_hf != NULL && vim_strchr(p_hf, '$') == NULL) { vim_path = (char *)p_hf; } + +#ifdef WIN32 + // Find runtime path relative to the nvim binary i.e. ../share/runtime + if (vim_path == NULL) { + char exe_name[MAXPATHL]; + size_t exe_name_len = MAXPATHL; + if (os_exepath(exe_name, &exe_name_len) == 0) { + char *path_end = (char *)path_tail_with_sep((char_u *)exe_name); + *path_end = '\0'; // remove the trailing "nvim.exe" + path_end = (char *)path_tail((char_u *)exe_name); + *path_end = '\0'; // remove the trailing "bin/" + if (append_path( + exe_name, + "share" _PATHSEPSTR "nvim" _PATHSEPSTR "runtime" _PATHSEPSTR, + MAXPATHL) == OK) { + vim_path = exe_name; + } + } + } +#endif + if (vim_path != NULL) { // remove the file name char *vim_path_end = (char *)path_tail((char_u *)vim_path); diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index 3c821936e9..4aa727733e 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -192,6 +192,18 @@ int os_nodetype(const char *name) return nodetype; } +/// Gets the absolute path of the currently running executable. +/// +/// @param[out] buffer Returns the path string. +/// @param[in] size Size of `buffer`. +/// +/// @return `0` on success, or libuv error code on failure. +int os_exepath(char *buffer, size_t *size) + FUNC_ATTR_NONNULL_ALL +{ + return uv_exepath(buffer, size); +} + /// Checks if the given path represents an executable file. /// /// @param[in] name Name of the executable. diff --git a/src/nvim/os/stdpaths.c b/src/nvim/os/stdpaths.c index 10b3f4c091..afb9bdec31 100644 --- a/src/nvim/os/stdpaths.c +++ b/src/nvim/os/stdpaths.c @@ -16,20 +16,29 @@ static const char *xdg_env_vars[] = { [kXDGDataDirs] = "XDG_DATA_DIRS", }; +#ifdef WIN32 +static const char *const xdg_defaults_env_vars[] = { + [kXDGConfigHome] = "LOCALAPPDATA", + [kXDGDataHome] = "LOCALAPPDATA", + [kXDGCacheHome] = "TEMP", + [kXDGRuntimeDir] = NULL, + [kXDGConfigDirs] = NULL, + [kXDGDataDirs] = NULL, +}; +#endif + /// Defaults for XDGVarType values /// /// Used in case environment variables contain nothing. Need to be expanded. static const char *const xdg_defaults[] = { #ifdef WIN32 - // Windows - [kXDGConfigHome] = "$LOCALAPPDATA", - [kXDGDataHome] = "$LOCALAPPDATA", - [kXDGCacheHome] = "$TEMP", + [kXDGConfigHome] = "~\\AppData\\Local", + [kXDGDataHome] = "~\\AppData\\Local", + [kXDGCacheHome] = "~\\AppData\\Local\\Temp", [kXDGRuntimeDir] = NULL, [kXDGConfigDirs] = NULL, [kXDGDataDirs] = NULL, #else - // Linux, BSD, CYGWIN, Apple [kXDGConfigHome] = "~/.config", [kXDGDataHome] = "~/.local/share", [kXDGCacheHome] = "~/.cache", @@ -50,7 +59,14 @@ char *stdpaths_get_xdg_var(const XDGVarType idx) const char *const env = xdg_env_vars[idx]; const char *const fallback = xdg_defaults[idx]; - const char *const env_val = os_getenv(env); + const char *env_val = os_getenv(env); + +#ifdef WIN32 + if (env_val == NULL) { + env_val = os_getenv(xdg_defaults_env_vars[idx]); + } +#endif + char *ret = NULL; if (env_val != NULL) { ret = xstrdup(env_val); diff --git a/src/nvim/po/uk.po b/src/nvim/po/uk.po index 3145931bfe..cff140508b 100644 --- a/src/nvim/po/uk.po +++ b/src/nvim/po/uk.po @@ -4424,8 +4424,8 @@ msgstr "" "перед записом: %s" #, c-format -msgid "E138: All %s.tmp.X files exist, cannot write ShaDa file!" -msgstr "E138: Усі файли %s.tmp.X зайнято, неможливо записати файл ShaDa!" +msgid "E929: All %s.tmp.X files exist, cannot write ShaDa file!" +msgstr "E929: Усі файли %s.tmp.X зайнято, неможливо записати файл ShaDa!" #, c-format msgid "System error while opening temporary ShaDa file %s for writing: %s" diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 41acc48f97..c0db076eff 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -4912,7 +4912,7 @@ void win_redr_status(win_T *wp) screen_fill(row, row + 1, len + wp->w_wincol, this_ru_col + wp->w_wincol, fillchar, fillchar, attr); - if (get_keymap_str(wp, NameBuff, MAXPATHL) + if (get_keymap_str(wp, (char_u *)"<%s>", NameBuff, MAXPATHL) && this_ru_col - len > (int)(STRLEN(NameBuff) + 1)) screen_puts(NameBuff, row, (int)(this_ru_col - STRLEN(NameBuff) - 1 + wp->w_wincol), attr); @@ -4993,8 +4993,9 @@ int stl_connected(win_T *wp) int get_keymap_str ( win_T *wp, - char_u *buf, /* buffer for the result */ - int len /* length of buffer */ + char_u *fmt, // format string containing one %s item + char_u *buf, // buffer for the result + int len // length of buffer ) { char_u *p; @@ -5021,10 +5022,9 @@ get_keymap_str ( else p = (char_u *)"lang"; } - if ((int)(STRLEN(p) + 3) < len) - sprintf((char *)buf, "<%s>", p); - else + if (vim_snprintf((char *)buf, len, (char *)fmt, p) > len - 1) { buf[0] = NUL; + } xfree(s); } return buf[0] != NUL; @@ -6752,10 +6752,12 @@ int showmode(void) if (p_fkmap) MSG_PUTS_ATTR(farsi_text_5, attr); if (State & LANGMAP) { - if (curwin->w_p_arab) + if (curwin->w_p_arab) { MSG_PUTS_ATTR(_(" Arabic"), attr); - else - MSG_PUTS_ATTR(_(" (lang)"), attr); + } else if (get_keymap_str(curwin, (char_u *)" (%s)", + NameBuff, MAXPATHL)) { + MSG_PUTS_ATTR(NameBuff, attr); + } } if ((State & INSERT) && p_paste) MSG_PUTS_ATTR(_(" (paste)"), attr); diff --git a/src/nvim/search.c b/src/nvim/search.c index 1029190db4..2a087276e7 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -3976,12 +3976,10 @@ current_search ( return OK; } -/* - * Check if the pattern is one character or zero-width. - * If move is true, check from the beginning of the buffer, - * else from the current cursor position. - * Returns TRUE, FALSE or -1 for failure. - */ +/// Check if the pattern is one character long or zero-width. +/// If move is true, check from the beginning of the buffer, +/// else from the current cursor position. +/// Returns TRUE, FALSE or -1 for failure. static int is_one_char(char_u *pattern, bool move) { regmmatch_T regmatch; @@ -3991,11 +3989,17 @@ static int is_one_char(char_u *pattern, bool move) int save_called_emsg = called_emsg; int flag = 0; + if (pattern == NULL) { + pattern = spats[last_idx].pat; + } + if (search_regcomp(pattern, RE_SEARCH, RE_SEARCH, SEARCH_KEEP, ®match) == FAIL) return -1; - /* move to match */ + // init startcol correctly + regmatch.startpos[0].col = -1; + // move to match if (move) { clearpos(&pos); } else { @@ -4003,21 +4007,29 @@ static int is_one_char(char_u *pattern, bool move) /* accept a match at the cursor position */ flag = SEARCH_START; } - if (searchit(curwin, curbuf, &pos, FORWARD, spats[last_idx].pat, 1, - SEARCH_KEEP + flag, RE_SEARCH, 0, NULL) != FAIL) { - /* Zero-width pattern should match somewhere, then we can check if - * start and end are in the same position. */ - called_emsg = FALSE; - nmatched = vim_regexec_multi(®match, curwin, curbuf, - pos.lnum, (colnr_T)0, NULL); - - if (!called_emsg) + if (searchit(curwin, curbuf, &pos, FORWARD, pattern, 1, + SEARCH_KEEP + flag, RE_SEARCH, 0, NULL) != FAIL) { + // Zero-width pattern should match somewhere, then we can check if + // start and end are in the same position. + called_emsg = false; + do { + regmatch.startpos[0].col++; + nmatched = vim_regexec_multi(®match, curwin, curbuf, + pos.lnum, regmatch.startpos[0].col, NULL); + if (!nmatched) { + break; + } + } while (regmatch.startpos[0].col < pos.col); + + if (!called_emsg) { result = (nmatched != 0 && regmatch.startpos[0].lnum == regmatch.endpos[0].lnum && regmatch.startpos[0].col == regmatch.endpos[0].col); - - if (!result && inc(&pos) >= 0 && pos.col == regmatch.endpos[0].col) - result = TRUE; + // one char width + if (!result && inc(&pos) >= 0 && pos.col == regmatch.endpos[0].col) { + result = true; + } + } } called_emsg |= save_called_emsg; diff --git a/src/nvim/shada.c b/src/nvim/shada.c index 867c697a9a..8cf5976e8b 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -118,9 +118,10 @@ KHASH_SET_INIT_STR(strset) // E576: Missing '>' // E577: Illegal register name // E886: Can't rename viminfo file to %s! +// E929: Too many viminfo temp files, like %s! // Now only six of them are used: // E137: ShaDa file is not writeable (for pre-open checks) -// E138: All %s.tmp.X files exist, cannot write ShaDa file! +// E929: All %s.tmp.X files exist, cannot write ShaDa file! // RCERR (E576) for critical read errors. // RNERR (E136) for various errors when renaming. // RERR (E575) for various errors inside read ShaDa file. @@ -148,6 +149,9 @@ KHASH_SET_INIT_STR(strset) /// Common prefix for all ignorable “write” errors #define WERR "E574: " +/// Callback function for add_search_pattern +typedef void (*SearchPatternGetter)(SearchPattern *); + /// Flags for shada_read_file and children typedef enum { kShaDaWantInfo = 1, ///< Load non-mark information @@ -2323,8 +2327,9 @@ static inline ShaDaWriteResult shada_read_when_writing( /// @param[in] removable_bufs Buffers which are ignored /// /// @return ShadaEntry List of buffers to save, kSDItemBufferList entry. -static ShadaEntry shada_get_buflist(khash_t(bufset) *const removable_bufs) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +static inline ShadaEntry shada_get_buflist( + khash_t(bufset) *const removable_bufs) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE { int max_bufs = get_shada_parameter('%'); size_t buf_count = 0; @@ -2368,6 +2373,62 @@ static ShadaEntry shada_get_buflist(khash_t(bufset) *const removable_bufs) return buflist_entry; } +/// Save search pattern to PossiblyFreedShadaEntry +/// +/// @param[out] ret_pse Location where result will be saved. +/// @param[in] get_pattern Function used to get pattern. +/// @param[in] is_substitute_pattern True if pattern in question is substitute +/// pattern. Also controls whether some +/// fields should be initialized to default +/// or values from get_pattern. +/// @param[in] search_last_used Result of search_was_last_used(). +/// @param[in] search_highlighted True if search pattern was highlighted by +/// &hlsearch and this information should be +/// saved. +static inline void add_search_pattern(PossiblyFreedShadaEntry *const ret_pse, + const SearchPatternGetter get_pattern, + const bool is_substitute_pattern, + const bool search_last_used, + const bool search_highlighted) + FUNC_ATTR_ALWAYS_INLINE +{ + const ShadaEntry defaults = sd_default_values[kSDItemSearchPattern]; + SearchPattern pat; + get_pattern(&pat); + if (pat.pat != NULL) { + *ret_pse = (PossiblyFreedShadaEntry) { + .can_free_entry = false, + .data = { + .type = kSDItemSearchPattern, + .timestamp = pat.timestamp, + .data = { + .search_pattern = { + .magic = pat.magic, + .smartcase = !pat.no_scs, + .has_line_offset = (is_substitute_pattern + ? defaults.data.search_pattern.has_line_offset + : pat.off.line), + .place_cursor_at_end = ( + is_substitute_pattern + ? defaults.data.search_pattern.place_cursor_at_end + : pat.off.end), + .offset = (is_substitute_pattern + ? defaults.data.search_pattern.offset + : pat.off.off), + .is_last_used = (is_substitute_pattern ^ search_last_used), + .is_substitute_pattern = is_substitute_pattern, + .highlighted = ((is_substitute_pattern ^ search_last_used) + && search_highlighted), + .pat = (char *)pat.pat, + .additional_data = pat.additional_data, + .search_backward = (!is_substitute_pattern && pat.off.dir == '?'), + } + } + } + }; + } +} + /// Write ShaDa file /// /// @param[in] sd_writer Structure containing file writer definition. @@ -2529,45 +2590,14 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, const bool search_highlighted = !(no_hlsearch || find_shada_parameter('h') != NULL); const bool search_last_used = search_was_last_used(); -#define ADD_SEARCH_PAT(func, wms_attr, hlo, pcae, o, is_sub) \ - do { \ - SearchPattern pat; \ - func(&pat); \ - if (pat.pat != NULL) { \ - wms->wms_attr = (PossiblyFreedShadaEntry) { \ - .can_free_entry = false, \ - .data = { \ - .type = kSDItemSearchPattern, \ - .timestamp = pat.timestamp, \ - .data = { \ - .search_pattern = { \ - .magic = pat.magic, \ - .smartcase = !pat.no_scs, \ - .has_line_offset = hlo, \ - .place_cursor_at_end = pcae, \ - .offset = o, \ - .is_last_used = (is_sub ^ search_last_used), \ - .is_substitute_pattern = is_sub, \ - .highlighted = ((is_sub ^ search_last_used) \ - && search_highlighted), \ - .pat = (char *) pat.pat, \ - .additional_data = pat.additional_data, \ - .search_backward = (!is_sub && pat.off.dir == '?'), \ - } \ - } \ - } \ - }; \ - } \ - } while (0) // Initialize search pattern - ADD_SEARCH_PAT(get_search_pattern, search_pattern, pat.off.line, \ - pat.off.end, pat.off.off, false); + add_search_pattern(&wms->search_pattern, &get_search_pattern, false, + search_last_used, search_highlighted); // Initialize substitute search pattern - ADD_SEARCH_PAT(get_substitute_pattern, sub_search_pattern, false, false, 0, - true); -#undef ADD_SEARCH_PAT + add_search_pattern(&wms->sub_search_pattern, &get_substitute_pattern, true, + search_last_used, search_highlighted); // Initialize substitute replacement string { @@ -2590,10 +2620,12 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, // Initialize jump list const void *jump_iter = NULL; + setpcmark(); + cleanup_jumplist(); do { xfmark_T fm; - cleanup_jumplist(); jump_iter = mark_jumplist_iter(jump_iter, curwin, &fm); + const buf_T *const buf = (fm.fmark.fnum == 0 ? NULL : buflist_findnr(fm.fmark.fnum)); diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index 612071e2e2..814c0bc3cb 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -34,6 +34,7 @@ NEW_TESTS = \ test_cmdline.res \ test_cscope.res \ test_diffmode.res \ + test_gn.res \ test_hardcopy.res \ test_help_tagjump.res \ test_history.res \ diff --git a/src/nvim/testdir/test17.in b/src/nvim/testdir/test17.in index 83abe17770..1a4ac6b6d1 100644 --- a/src/nvim/testdir/test17.in +++ b/src/nvim/testdir/test17.in @@ -4,13 +4,7 @@ Tests for: STARTTEST :set isfname=@,48-57,/,.,-,_,+,,,$,:,~,{,} -:function! DeleteDirectory(dir) -: if has("win16") || has("win32") || has("win64") || has("dos16") || has("dos32") -: exec "silent !rmdir /Q /S " . a:dir -: else -: exec "silent !rm -rf " . a:dir -: endif -:endfun +:" :if has("unix") :let $CDIR = "." /CDIR @@ -36,7 +30,7 @@ STARTTEST :" check for 'include' without \zs or \ze :lang C :call delete("./Xbase.a") -:call DeleteDirectory("Xdir1") +:call delete("Xdir1", "rf") :!mkdir Xdir1 :!mkdir "Xdir1/dir2" :e! Xdir1/dir2/foo.a @@ -61,7 +55,7 @@ ENDTEST STARTTEST :" check for 'include' with \zs and \ze :call delete("./Xbase.b") -:call DeleteDirectory("Xdir1") +:call delete("Xdir1", "rf") :!mkdir Xdir1 :!mkdir "Xdir1/dir2" :let &include='^\s*%inc\s*/\zs[^/]\+\ze' @@ -91,7 +85,7 @@ ENDTEST STARTTEST :" check for 'include' with \zs and no \ze :call delete("./Xbase.c") -:call DeleteDirectory("Xdir1") +:call delete("Xdir1", "rf") :!mkdir Xdir1 :!mkdir "Xdir1/dir2" :let &include='^\s*%inc\s*\%([[:upper:]][^[:space:]]*\s\+\)\?\zs\S\+\ze' diff --git a/src/nvim/testdir/test53.in b/src/nvim/testdir/test53.in index f3778c5192..20b5d019af 100644 --- a/src/nvim/testdir/test53.in +++ b/src/nvim/testdir/test53.in @@ -4,8 +4,6 @@ Note that the end-of-line moves the cursor to the next test line. Also test match() and matchstr() -Also test the gn command and repeating it. - STARTTEST /^start:/ da" @@ -52,35 +50,6 @@ dit :put =match('abc', '\zs', 2, 1) " 2 :put =match('abc', '\zs', 3, 1) " 3 :put =match('abc', '\zs', 4, 1) " -1 -/^foobar -gncsearchmatch/one\_s*two\_s -:1 -gnd -/[a]bcdx -:1 -2gnd/join -/$ -0gnd -/\>\zs -0gnd/^ -gnd$h/\zs -gnd/[u]niquepattern/s -vlgnd -/mother -:set selection=exclusive -$cgNmongoose/i -cgnj -:" Make sure there is no other match y uppercase. -/x59 -gggnd -:" test repeating dgn -/^Johnny -ggdgn. -:" test repeating gUgn -/^Depp -gggUgn. -gg/a:0\@!\zs\d\+ -nygnop :/^start:/,/^end:/wq! test.out ENDTEST @@ -102,32 +71,4 @@ innertext object </b> </begin> SEARCH: -foobar -one -two -abcdx | abcdx | abcdx -join -lines -zero width pattern -delete first and last chars -uniquepattern uniquepattern -my very excellent mother just served us nachos -for (i=0; i<=10; i++) -a:10 - -a:1 - -a:20 -Y -text -Y ---1 -Johnny ---2 -Johnny ---3 -Depp ---4 -Depp ---5 end: diff --git a/src/nvim/testdir/test53.ok b/src/nvim/testdir/test53.ok index 05206972a4..d57d86bbb0 100644 --- a/src/nvim/testdir/test53.ok +++ b/src/nvim/testdir/test53.ok @@ -42,30 +42,4 @@ a 3 -1 SEARCH: -searchmatch -abcdx | | abcdx -join lines -zerowidth pattern -elete first and last char - uniquepattern -my very excellent mongoose just served us nachos -for (j=0; i<=10; i++) -a:10 - -a:1 -1 - -a:20 - -text -Y ---1 - ---2 - ---3 -DEPP ---4 -DEPP ---5 end: diff --git a/src/nvim/testdir/test73.in b/src/nvim/testdir/test73.in index 7d6c7287a5..9d50f7a789 100644 --- a/src/nvim/testdir/test73.in +++ b/src/nvim/testdir/test73.in @@ -8,16 +8,9 @@ STARTTEST :" This will cause a few errors, do it silently. :set visualbell :" -:function! DeleteDirectory(dir) -: if has("win16") || has("win32") || has("win64") || has("dos16") || has("dos32") -: exec "silent !rmdir /Q /S " . a:dir -: else -: exec "silent !rm -rf " . a:dir -: endif -:endfun :" On windows a stale "Xfind" directory may exist, remove it so that :" we start from a clean state. -:call DeleteDirectory("Xfind") +:call delete("Xfind", "rf") :new :let cwd=getcwd() :let test_out = cwd . '/test.out' @@ -169,7 +162,7 @@ SVoyager 2:w :exec "w >>" . test_out :q :exec "cd " . cwd -:call DeleteDirectory("Xfind") +:call delete("Xfind", "rf") :qa! ENDTEST diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim index 818ff7cf54..60248bf430 100644 --- a/src/nvim/testdir/test_alot.vim +++ b/src/nvim/testdir/test_alot.vim @@ -4,6 +4,7 @@ source test_assign.vim source test_autocmd.vim source test_cursor_func.vim +source test_execute_func.vim source test_ex_undo.vim source test_expr.vim source test_expr_utf8.vim diff --git a/src/nvim/testdir/test_execute_func.vim b/src/nvim/testdir/test_execute_func.vim new file mode 100644 index 0000000000..6f61bede93 --- /dev/null +++ b/src/nvim/testdir/test_execute_func.vim @@ -0,0 +1,55 @@ +" test execute() + +func NestedEval() + let nested = execute('echo "nested\nlines"') + echo 'got: "' . nested . '"' +endfunc + +func NestedRedir() + redir => var + echo 'broken' + redir END +endfunc + +func Test_execute_string() + call assert_equal("\nnocompatible", execute('set compatible?')) + call assert_equal("\nsomething\nnice", execute('echo "something\nnice"')) + call assert_equal("noendofline", execute('echon "noendofline"')) + call assert_equal("", execute(123)) + + call assert_equal("\ngot: \"\nnested\nlines\"", execute('call NestedEval()')) + redir => redired + echo 'this' + let evaled = execute('echo "that"') + echo 'theend' + redir END +" Nvim supports execute('... :redir ...'), so this test is intentionally +" disabled. +" call assert_equal("\nthis\ntheend", redired) + call assert_equal("\nthat", evaled) + + call assert_fails('call execute("doesnotexist")', 'E492:') + call assert_fails('call execute(3.4)', 'E806:') +" Nvim supports execute('... :redir ...'), so this test is intentionally +" disabled. +" call assert_fails('call execute("call NestedRedir()")', 'E930:') + + call assert_equal("\nsomething", execute('echo "something"', '')) + call assert_equal("\nsomething", execute('echo "something"', 'silent')) + call assert_equal("\nsomething", execute('echo "something"', 'silent!')) + call assert_equal("", execute('burp', 'silent!')) + call assert_fails('call execute("echo \"x\"", 3.4)', 'E806:') + + call assert_equal("", execute("")) +endfunc + +func Test_execute_list() + call assert_equal("\nsomething\nnice", execute(['echo "something"', 'echo "nice"'])) + let l = ['for n in range(0, 3)', + \ 'echo n', + \ 'endfor'] + call assert_equal("\n0\n1\n2\n3", execute(l)) + + call assert_equal("", execute([])) + call assert_equal("", execute(v:_null_list)) +endfunc diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim index 7483973fca..39dcacb55f 100644 --- a/src/nvim/testdir/test_expr.vim +++ b/src/nvim/testdir/test_expr.vim @@ -1,20 +1,5 @@ " Tests for expressions. -func Test_version() - call assert_true(has('patch-7.4.001')) - call assert_true(has('patch-7.4.01')) - call assert_true(has('patch-7.4.1')) - call assert_true(has('patch-6.9.999')) - call assert_true(has('patch-7.1.999')) - call assert_true(has('patch-7.4.123')) - - call assert_false(has('patch-7')) - call assert_false(has('patch-7.4')) - call assert_false(has('patch-7.4.')) - call assert_false(has('patch-9.1.0')) - call assert_false(has('patch-9.9.1')) -endfunc - func Test_equal() let base = {} func base.method() @@ -37,6 +22,35 @@ func Test_equal() call assert_fails('echo base.method > instance.method') endfunc +func Test_version() + call assert_true(has('patch-7.4.001')) + call assert_true(has('patch-7.4.01')) + call assert_true(has('patch-7.4.1')) + call assert_true(has('patch-6.9.999')) + call assert_true(has('patch-7.1.999')) + call assert_true(has('patch-7.4.123')) + + call assert_false(has('patch-7')) + call assert_false(has('patch-7.4')) + call assert_false(has('patch-7.4.')) + call assert_false(has('patch-9.1.0')) + call assert_false(has('patch-9.9.1')) +endfunc + +func Test_dict() + let d = {'': 'empty', 'a': 'a', 0: 'zero'} + call assert_equal('empty', d['']) + call assert_equal('a', d['a']) + call assert_equal('zero', d[0]) + call assert_true(has_key(d, '')) + call assert_true(has_key(d, 'a')) + + let d[''] = 'none' + let d['a'] = 'aaa' + call assert_equal('none', d['']) + call assert_equal('aaa', d['a']) +endfunc + func Test_strgetchar() call assert_equal(char2nr('a'), strgetchar('axb', 0)) call assert_equal(char2nr('x'), strgetchar('axb', 1)) @@ -61,20 +75,6 @@ func Test_strcharpart() call assert_equal('a', strcharpart('axb', -1, 2)) endfunc -func Test_dict() - let d = {'': 'empty', 'a': 'a', 0: 'zero'} - call assert_equal('empty', d['']) - call assert_equal('a', d['a']) - call assert_equal('zero', d[0]) - call assert_true(has_key(d, '')) - call assert_true(has_key(d, 'a')) - - let d[''] = 'none' - let d['a'] = 'aaa' - call assert_equal('none', d['']) - call assert_equal('aaa', d['a']) -endfunc - func Test_loop_over_null_list() let null_list = submatch(1, 1) for i in null_list @@ -92,3 +92,17 @@ endfunc func Test_set_reg_null_list() call setreg('x', v:_null_list) endfunc + +func Test_special_char() + " The failure is only visible using valgrind. + call assert_fails('echo "\<C-">') +endfunc + +func Test_setmatches() + hi def link 1 Comment + hi def link 2 PreProc + let set = [{"group": 1, "pattern": 2, "id": 3, "priority": 4, "conceal": 5}] + let exp = [{"group": '1', "pattern": '2', "id": 3, "priority": 4, "conceal": '5'}] + call setmatches(set) + call assert_equal(exp, getmatches()) +endfunc diff --git a/src/nvim/testdir/test_gn.vim b/src/nvim/testdir/test_gn.vim new file mode 100644 index 0000000000..3eca99bd99 --- /dev/null +++ b/src/nvim/testdir/test_gn.vim @@ -0,0 +1,93 @@ +" Test for gn command + +func Test_gn_command() + noa new + " replace a single char by itsself quoted: + call setline('.', 'abc x def x ghi x jkl') + let @/='x' + exe "norm! cgn'x'\<esc>.." + call assert_equal("abc 'x' def 'x' ghi 'x' jkl", getline('.')) + sil! %d_ + " simple search match + call setline('.', 'foobar') + let @/='foobar' + exe "norm! gncsearchmatch" + call assert_equal('searchmatch', getline('.')) + sil! %d _ + " replace a multi-line match + call setline('.', ['', 'one', 'two']) + let @/='one\_s*two\_s' + exe "norm! gnceins\<CR>zwei" + call assert_equal(['','eins','zwei'], getline(1,'$')) + sil! %d _ + " test count argument + call setline('.', ['', 'abcdx | abcdx | abcdx']) + let @/='[a]bcdx' + exe "norm! 2gnd" + call assert_equal(['','abcdx | | abcdx'], getline(1,'$')) + sil! %d _ + " join lines + call setline('.', ['join ', 'lines']) + let @/='$' + exe "norm! 0gnd" + call assert_equal(['join lines'], getline(1,'$')) + sil! %d _ + " zero-width match + call setline('.', ['', 'zero width pattern']) + let @/='\>\zs' + exe "norm! 0gnd" + call assert_equal(['', 'zerowidth pattern'], getline(1,'$')) + sil! %d _ + " delete first and last chars + call setline('.', ['delete first and last chars']) + let @/='^' + exe "norm! 0gnd$" + let @/='\zs' + exe "norm! gnd" + call assert_equal(['elete first and last char'], getline(1,'$')) + sil! %d _ + " using visual mode + call setline('.', ['', 'uniquepattern uniquepattern']) + exe "norm! /[u]niquepattern/s\<cr>vlgnd" + call assert_equal(['', ' uniquepattern'], getline(1,'$')) + sil! %d _ + " backwards search + call setline('.', ['my very excellent mother just served us nachos']) + let @/='mother' + exe "norm! $cgNmongoose" + call assert_equal(['my very excellent mongoose just served us nachos'], getline(1,'$')) + sil! %d _ + " search for single char + call setline('.', ['','for (i=0; i<=10; i++)']) + let @/='i' + exe "norm! cgnj" + call assert_equal(['','for (j=0; i<=10; i++)'], getline(1,'$')) + sil! %d _ + " search hex char + call setline('.', ['','Y']) + set noignorecase + let @/='\%x59' + exe "norm! gnd" + call assert_equal(['',''], getline(1,'$')) + sil! %d _ + " test repeating gdn + call setline('.', ['', '1', 'Johnny', '2', 'Johnny', '3']) + let @/='Johnny' + exe "norm! dgn." + call assert_equal(['','1', '', '2', '', '3'], getline(1,'$')) + sil! %d _ + " test repeating gUgn + call setline('.', ['', '1', 'Depp', '2', 'Depp', '3']) + let @/='Depp' + exe "norm! gUgn." + call assert_equal(['', '1', 'DEPP', '2', 'DEPP', '3'], getline(1,'$')) + sil! %d _ + " test using look-ahead assertions + call setline('.', ['a:10', '', 'a:1', '', 'a:20']) + let @/='a:0\@!\zs\d\+' + exe "norm! 2nygno\<esc>p" + call assert_equal(['a:10', '', 'a:1', '1', '', 'a:20'], getline(1,'$')) + sil! %d _ +endfu + +" vim: tabstop=2 shiftwidth=0 expandtab diff --git a/src/nvim/testdir/test_goto.vim b/src/nvim/testdir/test_goto.vim index 2afd96b296..b6ac5720c3 100644 --- a/src/nvim/testdir/test_goto.vim +++ b/src/nvim/testdir/test_goto.vim @@ -18,3 +18,18 @@ func Test_gee_dee() call assert_equal(14, col('.')) quit! endfunc + +" Check that setting 'cursorline' does not change curswant +func Test_cursorline_keep_col() + new + call setline(1, ['long long long line', 'short line']) + normal ggfi + let pos = getcurpos() + normal j + set cursorline + normal k + call assert_equal(pos, getcurpos()) + bwipe! + set nocursorline +endfunc + diff --git a/src/nvim/testdir/test_syn_attr.vim b/src/nvim/testdir/test_syn_attr.vim index 94d6b8735f..64aab3411d 100644 --- a/src/nvim/testdir/test_syn_attr.vim +++ b/src/nvim/testdir/test_syn_attr.vim @@ -20,7 +20,7 @@ func Test_missing_attr() if fontname == '' let fontname = 'something' endif - exe 'hi Mine guifg=blue guibg=red font=' . escape(fontname, ' \') + exe "hi Mine guifg=blue guibg=red font='" . fontname . "'" call assert_equal('blue', synIDattr(hlID("Mine"), "fg", 'gui')) call assert_equal('red', synIDattr(hlID("Mine"), "bg", 'gui')) call assert_equal(fontname, synIDattr(hlID("Mine"), "font", 'gui')) diff --git a/src/nvim/version.c b/src/nvim/version.c index f34473098c..acaad38ca2 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -280,7 +280,7 @@ static int included_patches[] = { 2163, 2162, // 2161, - // 2160, + 2160, // 2159, 2158, // 2157 NA @@ -340,8 +340,8 @@ static int included_patches[] = { 2103, // 2102 NA // 2101, - // 2100, - // 2099, + 2100, + 2099, // 2098, // 2097, // 2096, @@ -363,7 +363,7 @@ static int included_patches[] = { // 2080, // 2079 NA // 2078 NA - // 2077, + 2077, // 2076, 2075, // 2074, @@ -375,7 +375,7 @@ static int included_patches[] = { // 2068, // 2067, 2066, - // 2065, + 2065, // 2064, // 2063 NA // 2062, @@ -431,8 +431,8 @@ static int included_patches[] = { 2012, 2011, 2010, - // 2009, - // 2008, + 2009, + 2008, 2007, 2006, 2005, @@ -472,7 +472,7 @@ static int included_patches[] = { 1971, 1970, // 1969 NA - // 1968, + 1968, 1967, 1966, // 1965 NA @@ -515,7 +515,7 @@ static int included_patches[] = { 1928, // 1927 NA // 1926 NA - // 1925 NA + 1925, // 1924 NA 1923, // 1922 NA @@ -530,7 +530,7 @@ static int included_patches[] = { 1913, 1912, // 1911 NA - // 1910, + 1910, 1909, // 1908 NA // 1907 NA @@ -551,7 +551,7 @@ static int included_patches[] = { 1892, // 1891 NA // 1890 NA - // 1889, + 1889, // 1888 NA // 1887 NA // 1886 NA @@ -589,7 +589,7 @@ static int included_patches[] = { // 1854 NA // 1853 NA // 1852 NA - // 1851, + 1851, // 1850 NA // 1849 NA // 1848 NA |