diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/CMakeLists.txt | 3 | ||||
-rw-r--r-- | src/nvim/api/extmark.c | 5 | ||||
-rw-r--r-- | src/nvim/auevents.lua | 2 | ||||
-rw-r--r-- | src/nvim/buffer.c | 11 | ||||
-rw-r--r-- | src/nvim/decoration.c | 6 | ||||
-rw-r--r-- | src/nvim/drawline.c | 4 | ||||
-rw-r--r-- | src/nvim/eval/typval.c | 80 | ||||
-rw-r--r-- | src/nvim/eval/typval.h | 74 | ||||
-rw-r--r-- | src/nvim/eval/typval_defs.h | 21 | ||||
-rw-r--r-- | src/nvim/eval/vars.c | 348 | ||||
-rw-r--r-- | src/nvim/ex_cmds.c | 28 | ||||
-rw-r--r-- | src/nvim/ex_cmds.lua | 2 | ||||
-rw-r--r-- | src/nvim/ex_getln.c | 2 | ||||
-rw-r--r-- | src/nvim/fileio.c | 63 | ||||
-rw-r--r-- | src/nvim/main.c | 5 | ||||
-rw-r--r-- | src/nvim/memfile.c | 19 | ||||
-rw-r--r-- | src/nvim/memfile_defs.h | 68 | ||||
-rw-r--r-- | src/nvim/memline.c | 4 | ||||
-rw-r--r-- | src/nvim/memory.c | 1 | ||||
-rw-r--r-- | src/nvim/ops.c | 9 | ||||
-rw-r--r-- | src/nvim/ops.h | 9 | ||||
-rw-r--r-- | src/nvim/os/stdpaths.c | 13 | ||||
-rw-r--r-- | src/nvim/po/check.vim | 12 | ||||
-rw-r--r-- | src/nvim/statusline.c | 39 | ||||
-rw-r--r-- | src/nvim/testing.c | 1 |
25 files changed, 379 insertions, 450 deletions
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 47f32abc07..222b283a5d 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -394,9 +394,6 @@ endif() if($ENV{CI}) # Don't debug log on CI, it gets too verbose in the main build log. # TODO(bfredl): debug log level also exposes some errors with EXITFREE in ASAN build. - set(LOG_DEBUG FALSE) -elseif(LOG_DEBUG) - target_compile_definitions(nvim PRIVATE NVIM_LOG_DEBUG) else() # Minimize logging for release-type builds. target_compile_definitions(nvim PRIVATE $<$<CONFIG:Debug>:NVIM_LOG_DEBUG>) diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 299413e510..aca290494b 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -483,8 +483,9 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// window column (starting from the first /// text column) /// - virt_text_hide : hide the virtual text when the background -/// text is selected or hidden due to -/// horizontal scroll 'nowrap' +/// text is selected or hidden because of +/// scrolling with 'nowrap' or 'smoothscroll'. +/// Currently only affects "overlay" virt_text. /// - hl_mode : control how highlights are combined with the /// highlights of the text. Currently only affects /// virt_text highlights, but might affect `hl_group` diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua index aef08be820..048b8d6631 100644 --- a/src/nvim/auevents.lua +++ b/src/nvim/auevents.lua @@ -72,6 +72,7 @@ return { 'InsertLeavePre', -- just before leaving Insert mode 'LspAttach', -- after an LSP client attaches to a buffer 'LspDetach', -- after an LSP client detaches from a buffer + 'LspRequest', -- after an LSP request is started, canceled, or completed 'LspTokenUpdate', -- after a visible LSP token is updated 'MenuPopup', -- just before popup menu is displayed 'ModeChanged', -- after changing the mode @@ -152,6 +153,7 @@ return { DiagnosticChanged=true, LspAttach=true, LspDetach=true, + LspRequest=true, LspTokenUpdate=true, RecordingEnter=true, RecordingLeave=true, diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index dab07487cd..bc52ab0771 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -248,6 +248,11 @@ int open_buffer(int read_stdin, exarg_T *eap, int flags_arg) return FAIL; } + // Do not sync this buffer yet, may first want to read the file. + if (curbuf->b_ml.ml_mfp != NULL) { + curbuf->b_ml.ml_mfp->mf_dirty = MF_DIRTY_YES_NOSYNC; + } + // The autocommands in readfile() may change the buffer, but only AFTER // reading the file. set_bufref(&old_curbuf, curbuf); @@ -316,6 +321,12 @@ int open_buffer(int read_stdin, exarg_T *eap, int flags_arg) } } + // Can now sync this buffer in ml_sync_all(). + if (curbuf->b_ml.ml_mfp != NULL + && curbuf->b_ml.ml_mfp->mf_dirty == MF_DIRTY_YES_NOSYNC) { + curbuf->b_ml.ml_mfp->mf_dirty = MF_DIRTY_YES; + } + // if first time loading this buffer, init b_chartab[] if (curbuf->b_flags & BF_NEVERLOADED) { (void)buf_init_chartab(curbuf, false); diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c index 81e1cb617c..2027848ccf 100644 --- a/src/nvim/decoration.c +++ b/src/nvim/decoration.c @@ -433,7 +433,9 @@ void decor_redraw_signs(buf_T *buf, int row, int *num_signs, SignTextAttrs sattr if (sattrs[j - 1].priority >= decor->priority) { break; } - sattrs[j] = sattrs[j - 1]; + if (j < SIGN_SHOW_MAX) { + sattrs[j] = sattrs[j - 1]; + } } if (j < SIGN_SHOW_MAX) { sattrs[j] = (SignTextAttrs) { @@ -603,7 +605,7 @@ int decor_virt_lines(win_T *wp, linenr_T lnum, VirtLines *lines, TriState has_fo mtkey_t mark = marktree_itr_current(itr); if (mark.pos.row < 0 || mark.pos.row >= end_row) { break; - } else if (marktree_decor_level(mark) < kDecorLevelVirtLine) { + } else if (mt_end(mark) || marktree_decor_level(mark) < kDecorLevelVirtLine) { goto next_mark; } bool above = mark.pos.row > (lnum - 2); diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index 557e58ff27..ced7e46287 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -1715,8 +1715,8 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl sign_idx = 0; wlv.draw_state = WL_LINE; if (has_decor && wlv.row == startrow + wlv.filler_lines) { - // hide virt_text on text hidden by 'nowrap' - decor_redraw_col(wp, (colnr_T)(ptr - line), wlv.off, true, &decor_state); + // hide virt_text on text hidden by 'nowrap' or 'smoothscroll' + decor_redraw_col(wp, (colnr_T)(ptr - line) - 1, wlv.off, true, &decor_state); } win_line_continue(&wlv); // use wlv.saved_ values } diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index cb8f8ce44d..42e9dc8f03 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -82,70 +82,6 @@ bool tv_in_free_unref_items = false; const char *const tv_empty_string = ""; //{{{1 Lists -//{{{2 List log -#ifdef LOG_LIST_ACTIONS -ListLog *list_log_first = NULL; -ListLog *list_log_last = NULL; - -/// Write list log to the given file -/// -/// @param[in] fname File to write log to. Will be appended to if already -/// present. -void list_write_log(const char *const fname) - FUNC_ATTR_NONNULL_ALL -{ - FileDescriptor fp; - const int fo_ret = file_open(&fp, fname, kFileCreate|kFileAppend, 0600); - if (fo_ret != 0) { - semsg(_("E5142: Failed to open file %s: %s"), fname, os_strerror(fo_ret)); - return; - } - for (ListLog *chunk = list_log_first; chunk != NULL;) { - for (size_t i = 0; i < chunk->size; i++) { - char buf[10 + 1 + ((16 + 3) * 3) + (8 + 2) + 2]; - // act : hex " c:" len "[]" "\n\0" - const ListLogEntry entry = chunk->entries[i]; - const size_t snp_len = (size_t)snprintf(buf, sizeof(buf), - "%-10.10s: l:%016" PRIxPTR "[%08d] 1:%016" PRIxPTR " 2:%016" PRIxPTR - "\n", - entry.action, entry.l, entry.len, entry.li1, - entry.li2); - assert(snp_len + 1 == sizeof(buf)); - const ptrdiff_t fw_ret = file_write(&fp, buf, snp_len); - if (fw_ret != (ptrdiff_t)snp_len) { - assert(fw_ret < 0); - if (i) { - memmove(chunk->entries, chunk->entries + i, - sizeof(chunk->entries[0]) * (chunk->size - i)); - chunk->size -= i; - } - semsg(_("E5143: Failed to write to file %s: %s"), - fname, os_strerror((int)fw_ret)); - return; - } - } - list_log_first = chunk->next; - xfree(chunk); - chunk = list_log_first; - } - const int fc_ret = file_close(&fp, true); - if (fc_ret != 0) { - semsg(_("E5144: Failed to close file %s: %s"), fname, os_strerror(fc_ret)); - } -} - -# ifdef EXITFREE -/// Free list log -void list_free_log(void) -{ - for (ListLog *chunk = list_log_first; chunk != NULL;) { - list_log_first = chunk->next; - xfree(chunk); - chunk = list_log_first; - } -} -# endif -#endif //{{{2 List item /// Allocate a list item @@ -252,7 +188,6 @@ list_T *tv_list_alloc(const ptrdiff_t len) list->lv_used_prev = NULL; list->lv_used_next = gc_first_list; gc_first_list = list; - list_log(list, NULL, (void *)(uintptr_t)len, "alloc"); list->lua_table_ref = LUA_NOREF; return list; } @@ -283,8 +218,6 @@ void tv_list_init_static10(staticList10_T *const sl) li->li_prev = li - 1; li->li_next = li + 1; } - list_log((const list_T *)sl, &sl->sl_items[0], &sl->sl_items[SL_SIZE - 1], - "s10init"); #undef SL_SIZE } @@ -296,7 +229,6 @@ void tv_list_init_static(list_T *const l) { CLEAR_POINTER(l); l->lv_refcount = DO_NOT_FREE_CNT; - list_log(l, NULL, NULL, "sinit"); } /// Free items contained in a list @@ -305,7 +237,6 @@ void tv_list_init_static(list_T *const l) void tv_list_free_contents(list_T *const l) FUNC_ATTR_NONNULL_ALL { - list_log(l, NULL, NULL, "freecont"); for (listitem_T *item = l->lv_first; item != NULL; item = l->lv_first) { // Remove the item before deleting it. l->lv_first = item->li_next; @@ -335,7 +266,6 @@ void tv_list_free_list(list_T *const l) if (l->lv_used_next != NULL) { l->lv_used_next->lv_used_prev = l->lv_used_prev; } - list_log(l, NULL, NULL, "freelist"); NLUA_CLEAR_REF(l->lua_table_ref); xfree(l); @@ -382,7 +312,6 @@ void tv_list_unref(list_T *const l) void tv_list_drop_items(list_T *const l, listitem_T *const item, listitem_T *const item2) FUNC_ATTR_NONNULL_ALL { - list_log(l, item, item2, "drop"); // Notify watchers. for (listitem_T *ip = item; ip != item2->li_next; ip = ip->li_next) { l->lv_len--; @@ -400,14 +329,12 @@ void tv_list_drop_items(list_T *const l, listitem_T *const item, listitem_T *con item->li_prev->li_next = item2->li_next; } l->lv_idx_item = NULL; - list_log(l, l->lv_first, l->lv_last, "afterdrop"); } /// Like tv_list_drop_items, but also frees all removed items void tv_list_remove_items(list_T *const l, listitem_T *const item, listitem_T *const item2) FUNC_ATTR_NONNULL_ALL { - list_log(l, item, item2, "remove"); tv_list_drop_items(l, item, item2); for (listitem_T *li = item;;) { tv_clear(TV_LIST_ITEM_TV(li)); @@ -431,7 +358,6 @@ void tv_list_move_items(list_T *const l, listitem_T *const item, listitem_T *con list_T *const tgt_l, const int cnt) FUNC_ATTR_NONNULL_ALL { - list_log(l, item, item2, "move"); tv_list_drop_items(l, item, item2); item->li_prev = tgt_l->lv_last; item2->li_next = NULL; @@ -442,7 +368,6 @@ void tv_list_move_items(list_T *const l, listitem_T *const item, listitem_T *con } tgt_l->lv_last = item2; tgt_l->lv_len += cnt; - list_log(tgt_l, tgt_l->lv_first, tgt_l->lv_last, "movetgt"); } /// Insert list item @@ -470,7 +395,6 @@ void tv_list_insert(list_T *const l, listitem_T *const ni, listitem_T *const ite } item->li_prev = ni; l->lv_len++; - list_log(l, ni, item, "insert"); } } @@ -496,7 +420,6 @@ void tv_list_insert_tv(list_T *const l, typval_T *const tv, listitem_T *const it void tv_list_append(list_T *const l, listitem_T *const item) FUNC_ATTR_NONNULL_ALL { - list_log(l, item, NULL, "append"); if (l->lv_last == NULL) { // empty list l->lv_first = item; @@ -1416,7 +1339,6 @@ void tv_list_reverse(list_T *const l) if (tv_list_len(l) <= 1) { return; } - list_log(l, NULL, NULL, "reverse"); #define SWAP(a, b) \ do { \ tmp = (a); \ @@ -1454,7 +1376,6 @@ void tv_list_item_sort(list_T *const l, ListSortItem *const ptrs, if (len <= 1) { return; } - list_log(l, NULL, NULL, "sort"); int i = 0; TV_LIST_ITER(l, li, { ptrs[i].item = li; @@ -1543,7 +1464,6 @@ listitem_T *tv_list_find(list_T *const l, int n) // Cache the used index. l->lv_idx = idx; l->lv_idx_item = item; - list_log(l, l->lv_idx_item, (void *)(uintptr_t)l->lv_idx, "find"); return item; } diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index 767fd706b3..e7b2499346 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -18,74 +18,6 @@ #include "nvim/message.h" #include "nvim/types.h" -#ifdef LOG_LIST_ACTIONS -# include "nvim/memory.h" - -extern ListLog *list_log_first; ///< First list log chunk, NULL if missing -extern ListLog *list_log_last; ///< Last list log chunk - -static inline ListLog *list_log_alloc(const size_t size) - REAL_FATTR_ALWAYS_INLINE REAL_FATTR_WARN_UNUSED_RESULT; - -/// Allocate a new log chunk and update globals -/// -/// @param[in] size Number of entries in a new chunk. -/// -/// @return [allocated] Newly allocated chunk. -static inline ListLog *list_log_new(const size_t size) -{ - ListLog *ret = xmalloc(offsetof(ListLog, entries) - + size * sizeof(ret->entries[0])); - ret->size = 0; - ret->capacity = size; - ret->next = NULL; - if (list_log_first == NULL) { - list_log_first = ret; - } else { - list_log_last->next = ret; - } - list_log_last = ret; - return ret; -} - -static inline void list_log(const list_T *const l, const listitem_T *const li1, - const listitem_T *const li2, const char *const action) - REAL_FATTR_ALWAYS_INLINE; - -/// Add new entry to log -/// -/// If last chunk was filled it uses twice as much memory to allocate the next -/// chunk. -/// -/// @param[in] l List to which entry belongs. -/// @param[in] li1 List item 1. -/// @param[in] li2 List item 2, often used for integers and not list items. -/// @param[in] action Logged action. -static inline void list_log(const list_T *const l, const listitem_T *const li1, - const listitem_T *const li2, const char *const action) -{ - ListLog *tgt; - if (list_log_first == NULL) { - tgt = list_log_new(128); - } else if (list_log_last->size == list_log_last->capacity) { - tgt = list_log_new(list_log_last->capacity * 2); - } else { - tgt = list_log_last; - } - tgt->entries[tgt->size++] = (ListLogEntry) { - .l = (uintptr_t)l, - .li1 = (uintptr_t)li1, - .li2 = (uintptr_t)li2, - .len = (l == NULL ? 0 : l->lv_len), - .action = action, - }; -} -#else -# define list_log(...) -# define list_write_log(...) -# define list_free_log() -#endif - // In a hashtab item "hi_key" points to "di_key" in a dictitem. // This avoids adding a pointer to the hashtab item. @@ -174,7 +106,6 @@ static inline int tv_list_len(const list_T *l) /// @param[in] l List to check. static inline int tv_list_len(const list_T *const l) { - list_log(l, NULL, NULL, "len"); if (l == NULL) { return 0; } @@ -258,10 +189,8 @@ static inline listitem_T *tv_list_first(const list_T *l) static inline listitem_T *tv_list_first(const list_T *const l) { if (l == NULL) { - list_log(l, NULL, NULL, "first"); return NULL; } - list_log(l, l->lv_first, NULL, "first"); return l->lv_first; } @@ -276,10 +205,8 @@ static inline listitem_T *tv_list_last(const list_T *l) static inline listitem_T *tv_list_last(const list_T *const l) { if (l == NULL) { - list_log(l, NULL, NULL, "last"); return NULL; } - list_log(l, l->lv_last, NULL, "last"); return l->lv_last; } @@ -416,7 +343,6 @@ extern bool tv_in_free_unref_items; #define _TV_LIST_ITER_MOD(modifier, l, li, code) \ do { \ modifier list_T *const l_ = (l); \ - list_log(l_, NULL, NULL, "iter" #modifier); \ if (l_ != NULL) { \ for (modifier listitem_T *li = l_->lv_first; \ li != NULL; li = li->li_next) { \ diff --git a/src/nvim/eval/typval_defs.h b/src/nvim/eval/typval_defs.h index 3e49417f6f..767603ac0e 100644 --- a/src/nvim/eval/typval_defs.h +++ b/src/nvim/eval/typval_defs.h @@ -375,25 +375,4 @@ typedef struct { typedef int (*ListSorter)(const void *, const void *); -#ifdef LOG_LIST_ACTIONS -/// List actions log entry -typedef struct { - uintptr_t l; ///< List log entry belongs to. - uintptr_t li1; ///< First list item log entry belongs to, if applicable. - uintptr_t li2; ///< Second list item log entry belongs to, if applicable. - int len; ///< List length when log entry was created. - const char *action; ///< Logged action. -} ListLogEntry; - -typedef struct list_log ListLog; - -/// List actions log -struct list_log { - ListLog *next; ///< Next chunk or NULL. - size_t capacity; ///< Number of entries in current chunk. - size_t size; ///< Current chunk size. - ListLogEntry entries[]; ///< Actual log entries. -}; -#endif - #endif // NVIM_EVAL_TYPVAL_DEFS_H diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 64177d13e8..21b25b64f4 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -701,6 +701,189 @@ static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first) return arg; } +/// Set an environment variable, part of ex_let_one(). +static char *ex_let_env(char *arg, typval_T *const tv, const bool is_const, + const char *const endchars, const char *const op) + FUNC_ATTR_NONNULL_ARG(1, 2) FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (is_const) { + emsg(_("E996: Cannot lock an environment variable")); + return NULL; + } + + // Find the end of the name. + char *arg_end = NULL; + arg++; + char *name = arg; + int len = get_env_len((const char **)&arg); + if (len == 0) { + semsg(_(e_invarg2), name - 1); + } else { + if (op != NULL && vim_strchr("+-*/%", (uint8_t)(*op)) != NULL) { + semsg(_(e_letwrong), op); + } else if (endchars != NULL + && vim_strchr(endchars, (uint8_t)(*skipwhite(arg))) == NULL) { + emsg(_(e_letunexp)); + } else if (!check_secure()) { + char *tofree = NULL; + const char c1 = name[len]; + name[len] = NUL; + const char *p = tv_get_string_chk(tv); + if (p != NULL && op != NULL && *op == '.') { + char *s = vim_getenv(name); + if (s != NULL) { + tofree = concat_str(s, p); + p = tofree; + xfree(s); + } + } + if (p != NULL) { + vim_setenv_ext(name, p); + arg_end = arg; + } + name[len] = c1; + xfree(tofree); + } + } + return arg_end; +} + +/// Set an option, part of ex_let_one(). +static char *ex_let_option(char *arg, typval_T *const tv, const bool is_const, + const char *const endchars, const char *const op) + FUNC_ATTR_NONNULL_ARG(1, 2) FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (is_const) { + emsg(_("E996: Cannot lock an option")); + return NULL; + } + + // Find the end of the name. + char *arg_end = NULL; + int scope; + char *const p = (char *)find_option_end((const char **)&arg, &scope); + if (p == NULL + || (endchars != NULL + && vim_strchr(endchars, (uint8_t)(*skipwhite(p))) == NULL)) { + emsg(_(e_letunexp)); + } else { + varnumber_T n = 0; + getoption_T opt_type; + long numval; + char *stringval = NULL; + const char *s = NULL; + bool failed = false; + uint32_t opt_p_flags; + char *tofree = NULL; + + const char c1 = *p; + *p = NUL; + + opt_type = get_option_value(arg, &numval, &stringval, &opt_p_flags, scope); + if (opt_type == gov_bool + || opt_type == gov_number + || opt_type == gov_hidden_bool + || opt_type == gov_hidden_number) { + // number, possibly hidden + n = (long)tv_get_number(tv); + } + + if ((opt_p_flags & P_FUNC) && tv_is_func(*tv)) { + // If the option can be set to a function reference or a lambda + // and the passed value is a function reference, then convert it to + // the name (string) of the function reference. + s = tofree = encode_tv2string(tv, NULL); + } else if (tv->v_type != VAR_BOOL && tv->v_type != VAR_SPECIAL) { + // Avoid setting a string option to the text "v:false" or similar. + s = tv_get_string_chk(tv); + } + + if (op != NULL && *op != '=') { + if (((opt_type == gov_bool || opt_type == gov_number) && *op == '.') + || (opt_type == gov_string && *op != '.')) { + semsg(_(e_letwrong), op); + failed = true; // don't set the value + } else { + // number or bool + if (opt_type == gov_number || opt_type == gov_bool) { + switch (*op) { + case '+': + n = numval + n; break; + case '-': + n = numval - n; break; + case '*': + n = numval * n; break; + case '/': + n = num_divide(numval, n); break; + case '%': + n = num_modulus(numval, n); break; + } + s = NULL; + } else if (opt_type == gov_string && stringval != NULL && s != NULL) { + // string + char *const oldstringval = stringval; + stringval = concat_str(stringval, s); + xfree(oldstringval); + s = stringval; + } + } + } + + if (!failed) { + if (opt_type != gov_string || s != NULL) { + const char *err = set_option_value(arg, (long)n, s, scope); + arg_end = p; + if (err != NULL) { + emsg(_(err)); + } + } else { + emsg(_(e_stringreq)); + } + } + *p = c1; + xfree(stringval); + xfree(tofree); + } + return arg_end; +} + +/// Set a register, part of ex_let_one(). +static char *ex_let_register(char *arg, typval_T *const tv, const bool is_const, + const char *const endchars, const char *const op) + FUNC_ATTR_NONNULL_ARG(1, 2) FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (is_const) { + emsg(_("E996: Cannot lock a register")); + return NULL; + } + + char *arg_end = NULL; + arg++; + if (op != NULL && vim_strchr("+-*/%", (uint8_t)(*op)) != NULL) { + semsg(_(e_letwrong), op); + } else if (endchars != NULL + && vim_strchr(endchars, (uint8_t)(*skipwhite(arg + 1))) == NULL) { + emsg(_(e_letunexp)); + } else { + char *ptofree = NULL; + const char *p = tv_get_string_chk(tv); + if (p != NULL && op != NULL && *op == '.') { + char *s = get_reg_contents(*arg == '@' ? '"' : *arg, kGRegExprSrc); + if (s != NULL) { + ptofree = concat_str(s, p); + p = ptofree; + xfree(s); + } + } + if (p != NULL) { + write_reg_contents(*arg == '@' ? '"' : *arg, p, (ssize_t)strlen(p), false); + arg_end = arg + 1; + } + xfree(ptofree); + } + return arg_end; +} + /// Set one item of `:let var = expr` or `:let [v1, v2] = list` to its value /// /// @param[in] arg Start of the variable name. @@ -718,172 +901,21 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo { char *arg_end = NULL; - // ":let $VAR = expr": Set environment variable. if (*arg == '$') { - if (is_const) { - emsg(_("E996: Cannot lock an environment variable")); - return NULL; - } - // Find the end of the name. - arg++; - char *name = arg; - int len = get_env_len((const char **)&arg); - if (len == 0) { - semsg(_(e_invarg2), name - 1); - } else { - if (op != NULL && vim_strchr("+-*/%", (uint8_t)(*op)) != NULL) { - semsg(_(e_letwrong), op); - } else if (endchars != NULL - && vim_strchr(endchars, (uint8_t)(*skipwhite(arg))) == NULL) { - emsg(_(e_letunexp)); - } else if (!check_secure()) { - char *tofree = NULL; - const char c1 = name[len]; - name[len] = NUL; - const char *p = tv_get_string_chk(tv); - if (p != NULL && op != NULL && *op == '.') { - char *s = vim_getenv(name); - if (s != NULL) { - tofree = concat_str(s, p); - p = tofree; - xfree(s); - } - } - if (p != NULL) { - vim_setenv_ext(name, p); - arg_end = arg; - } - name[len] = c1; - xfree(tofree); - } - } + // ":let $VAR = expr": Set environment variable. + return ex_let_env(arg, tv, is_const, endchars, op); + } else if (*arg == '&') { // ":let &option = expr": Set option value. // ":let &l:option = expr": Set local option value. // ":let &g:option = expr": Set global option value. - } else if (*arg == '&') { - if (is_const) { - emsg(_("E996: Cannot lock an option")); - return NULL; - } - // Find the end of the name. - int scope; - char *const p = (char *)find_option_end((const char **)&arg, &scope); - if (p == NULL - || (endchars != NULL - && vim_strchr(endchars, (uint8_t)(*skipwhite(p))) == NULL)) { - emsg(_(e_letunexp)); - } else { - varnumber_T n = 0; - getoption_T opt_type; - long numval; - char *stringval = NULL; - const char *s = NULL; - bool failed = false; - uint32_t opt_p_flags; - char *tofree = NULL; - - const char c1 = *p; - *p = NUL; - - opt_type = get_option_value(arg, &numval, &stringval, &opt_p_flags, scope); - if (opt_type == gov_bool - || opt_type == gov_number - || opt_type == gov_hidden_bool - || opt_type == gov_hidden_number) { - // number, possibly hidden - n = (long)tv_get_number(tv); - } - - if ((opt_p_flags & P_FUNC) && tv_is_func(*tv)) { - // If the option can be set to a function reference or a lambda - // and the passed value is a function reference, then convert it to - // the name (string) of the function reference. - s = tofree = encode_tv2string(tv, NULL); - } else if (tv->v_type != VAR_BOOL && tv->v_type != VAR_SPECIAL) { - // Avoid setting a string option to the text "v:false" or similar. - s = tv_get_string_chk(tv); - } - - if (op != NULL && *op != '=') { - if (((opt_type == gov_bool || opt_type == gov_number) && *op == '.') - || (opt_type == gov_string && *op != '.')) { - semsg(_(e_letwrong), op); - failed = true; // don't set the value - } else { - // number or bool - if (opt_type == gov_number || opt_type == gov_bool) { - switch (*op) { - case '+': - n = numval + n; break; - case '-': - n = numval - n; break; - case '*': - n = numval * n; break; - case '/': - n = num_divide(numval, n); break; - case '%': - n = num_modulus(numval, n); break; - } - s = NULL; - } else if (opt_type == gov_string && stringval != NULL && s != NULL) { - // string - char *const oldstringval = stringval; - stringval = concat_str(stringval, s); - xfree(oldstringval); - s = stringval; - } - } - } - - if (!failed) { - if (opt_type != gov_string || s != NULL) { - const char *err = set_option_value(arg, (long)n, s, scope); - arg_end = p; - if (err != NULL) { - emsg(_(err)); - } - } else { - emsg(_(e_stringreq)); - } - } - *p = c1; - xfree(stringval); - xfree(tofree); - } - // ":let @r = expr": Set register contents. + return ex_let_option(arg, tv, is_const, endchars, op); } else if (*arg == '@') { - if (is_const) { - emsg(_("E996: Cannot lock a register")); - return NULL; - } - arg++; - if (op != NULL && vim_strchr("+-*/%", (uint8_t)(*op)) != NULL) { - semsg(_(e_letwrong), op); - } else if (endchars != NULL - && vim_strchr(endchars, (uint8_t)(*skipwhite(arg + 1))) == NULL) { - emsg(_(e_letunexp)); - } else { - char *ptofree = NULL; - const char *p = tv_get_string_chk(tv); - if (p != NULL && op != NULL && *op == '.') { - char *s = get_reg_contents(*arg == '@' ? '"' : *arg, kGRegExprSrc); - if (s != NULL) { - ptofree = concat_str(s, p); - p = ptofree; - xfree(s); - } - } - if (p != NULL) { - write_reg_contents(*arg == '@' ? '"' : *arg, p, (ssize_t)strlen(p), false); - arg_end = arg + 1; - } - xfree(ptofree); - } + // ":let @r = expr": Set register contents. + return ex_let_register(arg, tv, is_const, endchars, op); + } else if (eval_isnamec1(*arg) || *arg == '{') { // ":let var = expr": Set internal variable. // ":let {expr} = expr": Idem, name made with curly braces - } else if (eval_isnamec1(*arg) || *arg == '{') { lval_T lv; - char *const p = get_lval(arg, tv, &lv, false, false, 0, FNE_CHECK_START); if (p != NULL && lv.ll_name != NULL) { if (endchars != NULL && vim_strchr(endchars, (uint8_t)(*skipwhite(p))) == NULL) { diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 1d8c3c0cf4..40afb3250c 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -3280,9 +3280,11 @@ static int check_regexp_delim(int c) /// /// The usual escapes are supported as described in the regexp docs. /// -/// @param do_buf_event If `true`, send buffer updates. +/// @param cmdpreview_ns The namespace to show 'inccommand' preview highlights. +/// If <= 0, preview shouldn't be shown. /// @return 0, 1 or 2. See show_cmdpreview() for more information on what the return value means. -static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T cmdpreview_bufnr) +static int do_sub(exarg_T *eap, const proftime_T timeout, const long cmdpreview_ns, + const handle_T cmdpreview_bufnr) { #define ADJUST_SUB_FIRSTLNUM() \ do { \ @@ -3400,7 +3402,7 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T MB_PTR_ADV(cmd); } - if (!eap->skip && !cmdpreview) { + if (!eap->skip && cmdpreview_ns <= 0) { sub_set_replacement((SubReplacementString) { .sub = xstrdup(sub), .timestamp = os_time(), @@ -3420,7 +3422,7 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T endcolumn = (curwin->w_curswant == MAXCOL); } - if (sub != NULL && sub_joining_lines(eap, pat, sub, cmd, !cmdpreview)) { + if (sub != NULL && sub_joining_lines(eap, pat, sub, cmd, cmdpreview_ns <= 0)) { return 0; } @@ -3465,7 +3467,7 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T } if (search_regcomp(pat, NULL, RE_SUBST, which_pat, - (cmdpreview ? 0 : SEARCH_HIS), ®match) == FAIL) { + (cmdpreview_ns > 0 ? 0 : SEARCH_HIS), ®match) == FAIL) { if (subflags.do_error) { emsg(_(e_invcmd)); } @@ -3494,7 +3496,7 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T sub = xstrdup(sub); sub_copy = sub; } else { - char *newsub = regtilde(sub, magic_isset(), cmdpreview); + char *newsub = regtilde(sub, magic_isset(), cmdpreview_ns > 0); if (newsub != sub) { // newsub was allocated, free it later. sub_copy = newsub; @@ -3508,7 +3510,7 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T for (linenr_T lnum = eap->line1; lnum <= line2 && !got_quit && !aborting() - && (!cmdpreview || preview_lines.lines_needed <= (linenr_T)p_cwh + && (cmdpreview_ns <= 0 || preview_lines.lines_needed <= (linenr_T)p_cwh || lnum <= curwin->w_botline); lnum++) { long nmatch = vim_regexec_multi(®match, curwin, curbuf, lnum, @@ -3669,7 +3671,7 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T } } - if (subflags.do_ask && !cmdpreview) { + if (subflags.do_ask && cmdpreview_ns <= 0) { int typed = 0; // change State to MODE_CONFIRM, so that the mouse works @@ -3882,7 +3884,7 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T // Save the line numbers for the preview buffer // NOTE: If the pattern matches a final newline, the next line will // be shown also, but should not be highlighted. Intentional for now. - if (cmdpreview && !has_second_delim) { + if (cmdpreview_ns > 0 && !has_second_delim) { current_match.start.col = regmatch.startpos[0].col; if (current_match.end.lnum == 0) { current_match.end.lnum = sub_firstlnum + (linenr_T)nmatch - 1; @@ -3897,7 +3899,7 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T // 3. Substitute the string. During 'inccommand' preview only do this if // there is a replace pattern. - if (!cmdpreview || has_second_delim) { + if (cmdpreview_ns <= 0 || has_second_delim) { long lnum_start = lnum; // save the start lnum int save_ma = curbuf->b_p_ma; int save_sandbox = sandbox; @@ -4147,7 +4149,7 @@ skip: #define PUSH_PREVIEW_LINES() \ do { \ - if (cmdpreview) { \ + if (cmdpreview_ns > 0) { \ linenr_T match_lines = current_match.end.lnum \ - current_match.start.lnum +1; \ if (preview_lines.subresults.size > 0) { \ @@ -4230,7 +4232,7 @@ skip: beginline(BL_WHITE | BL_FIX); } } - if (!cmdpreview && !do_sub_msg(subflags.do_count) && subflags.do_ask && p_ch > 0) { + if (cmdpreview_ns <= 0 && !do_sub_msg(subflags.do_count) && subflags.do_ask && p_ch > 0) { msg(""); } } else { @@ -4269,7 +4271,7 @@ skip: int retv = 0; // Show 'inccommand' preview if there are matched lines. - if (cmdpreview && !aborting()) { + if (cmdpreview_ns > 0 && !aborting()) { if (got_quit || profile_passed_limit(timeout)) { // Too slow, disable. set_string_option_direct("icm", -1, "", OPT_FREE, SID_NONE); } else if (*p_icm != NUL && pat != NULL) { diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua index b0938fa711..88f3bc0b43 100644 --- a/src/nvim/ex_cmds.lua +++ b/src/nvim/ex_cmds.lua @@ -2212,7 +2212,7 @@ module.cmds = { }, { command='registers', - flags=bit.bor(EXTRA, NOTRLCOM, TRLBAR, CMDWIN, LOCK_OK), + flags=bit.bor(EXTRA, NOTRLCOM, TRLBAR, SBOXOK, CMDWIN, LOCK_OK), addr_type='ADDR_NONE', func='ex_display', }, diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index dd23f6ece9..b2acc561be 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -1597,7 +1597,7 @@ static int command_line_insert_reg(CommandLineState *s) bool literally = false; if (s->c != ESC) { // use ESC to cancel inserting register - literally = i == Ctrl_R; + literally = i == Ctrl_R || is_literal_register(s->c); cmdline_paste(s->c, literally, false); // When there was a serious error abort getting the diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index e60007bf88..d24ac1c233 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -153,6 +153,7 @@ void filemess(buf_T *buf, char *name, char *s, int attr) int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip, linenr_T lines_to_read, exarg_T *eap, int flags, bool silent) { + int retval = FAIL; // jump to "theend" instead of returning int fd = stdin_fd >= 0 ? stdin_fd : 0; int newfile = (flags & READ_NEW); int check_readonly; @@ -240,7 +241,7 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip, && vim_strchr(p_cpo, CPO_FNAMER) != NULL && !(flags & READ_DUMMY)) { if (set_rw_fname(fname, sfname) == FAIL) { - return FAIL; + goto theend; } } @@ -284,10 +285,9 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip, if (newfile) { if (apply_autocmds_exarg(EVENT_BUFREADCMD, NULL, sfname, false, curbuf, eap)) { - int status = OK; - + retval = OK; if (aborting()) { - status = FAIL; + retval = FAIL; } // The BufReadCmd code usually uses ":read" to get the text and @@ -295,14 +295,15 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip, // consider this to work like ":edit", thus reset the // BF_NOTEDITED flag. Then ":write" will work to overwrite the // same file. - if (status == OK) { + if (retval == OK) { curbuf->b_flags &= ~BF_NOTEDITED; } - return status; + goto theend; } } else if (apply_autocmds_exarg(EVENT_FILEREADCMD, sfname, sfname, false, NULL, eap)) { - return aborting() ? FAIL : OK; + retval = aborting() ? FAIL : OK; + goto theend; } curbuf->b_op_start = orig_start; @@ -310,7 +311,8 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip, if (flags & READ_NOFILE) { // Return NOTDONE instead of FAIL so that BufEnter can be triggered // and other operations don't fail. - return NOTDONE; + retval = NOTDONE; + goto theend; } } @@ -328,7 +330,7 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip, filemess(curbuf, fname, _("Illegal file name"), 0); msg_end(); msg_scroll = msg_save; - return FAIL; + goto theend; } // If the name ends in a path separator, we can't open it. Check here, @@ -340,7 +342,8 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip, } msg_end(); msg_scroll = msg_save; - return NOTDONE; + retval = NOTDONE; + goto theend; } } @@ -365,12 +368,13 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip, if (!silent) { filemess(curbuf, fname, _(msg_is_a_directory), 0); } + retval = NOTDONE; } else { filemess(curbuf, fname, _("is not a file"), 0); } msg_end(); msg_scroll = msg_save; - return S_ISDIR(perm) ? NOTDONE : FAIL; + goto theend; } } @@ -431,7 +435,7 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip, if (fd < 0) { // cannot open at all msg_scroll = msg_save; if (!newfile) { - return FAIL; + goto theend; } if (perm == UV_ENOENT) { // check if the file exists // Set the 'new-file' flag, so that when the file has @@ -450,7 +454,7 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip, || (using_b_fname && (old_b_fname != curbuf->b_fname))) { emsg(_(e_auchangedbuf)); - return FAIL; + goto theend; } } if (!silent) { @@ -472,10 +476,10 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip, // remember the current fileformat save_file_ff(curbuf); - if (aborting()) { // autocmds may abort script processing - return FAIL; + if (!aborting()) { // autocmds may abort script processing + retval = OK; // a new file is not an error } - return OK; // a new file is not an error + goto theend; } #if defined(UNIX) && defined(EOVERFLOW) filemess(curbuf, sfname, ((fd == UV_EFBIG) ? _("[File too big]") : @@ -491,7 +495,7 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip, #endif curbuf->b_p_ro = true; // must use "w!" now - return FAIL; + goto theend; } // Only set the 'ro' flag for readonly files the first time they are @@ -526,7 +530,7 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip, if (!read_buffer) { close(fd); } - return FAIL; + goto theend; } #ifdef UNIX // Set swap file protection bits after creating it. @@ -561,7 +565,7 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip, if (!read_buffer && !read_stdin) { close(fd); } - return FAIL; + goto theend; } no_wait_return++; // don't wait for return yet @@ -617,7 +621,7 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip, no_wait_return--; msg_scroll = msg_save; curbuf->b_p_ro = true; // must use "w!" now - return FAIL; + goto theend; } // Don't allow the autocommands to change the current buffer. // Try to re-open the file. @@ -636,7 +640,7 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip, emsg(_("E201: *ReadPre autocommands must not change current buffer")); } curbuf->b_p_ro = true; // must use "w!" now - return FAIL; + goto theend; } } @@ -1684,7 +1688,8 @@ failed: } msg_scroll = msg_save; check_marks_read(); - return OK; // an interrupt isn't really an error + retval = OK; // an interrupt isn't really an error + goto theend; } if (!filtering && !(flags & READ_DUMMY) && !silent) { @@ -1860,10 +1865,18 @@ failed: } } - if (recoverymode && error) { - return FAIL; + if (!(recoverymode && error)) { + retval = OK; } - return OK; + +theend: + if (curbuf->b_ml.ml_mfp != NULL + && curbuf->b_ml.ml_mfp->mf_dirty == MF_DIRTY_YES_NOSYNC) { + // OK to sync the swap file now + curbuf->b_ml.ml_mfp->mf_dirty = MF_DIRTY_YES; + } + + return retval; } #ifdef OPEN_CHR_FILES diff --git a/src/nvim/main.c b/src/nvim/main.c index d4fbf8ce93..f27ebb2f67 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -250,6 +250,11 @@ int main(int argc, char **argv) argv0 = argv[0]; + if (!appname_is_valid()) { + os_errmsg("$NVIM_APPNAME is not a valid file name.\n"); + exit(1); + } + if (argc > 1 && STRICMP(argv[1], "-ll") == 0) { if (argc == 2) { print_mainerr(err_arg_missing, argv[1]); diff --git a/src/nvim/memfile.c b/src/nvim/memfile.c index bd8314c679..5d6c58c387 100644 --- a/src/nvim/memfile.c +++ b/src/nvim/memfile.c @@ -103,7 +103,7 @@ memfile_T *mf_open(char *fname, int flags) mfp->mf_free_first = NULL; // free list is empty mfp->mf_used_first = NULL; // used list is empty mfp->mf_used_last = NULL; - mfp->mf_dirty = false; + mfp->mf_dirty = MF_DIRTY_NO; mf_hash_init(&mfp->mf_hash); mf_hash_init(&mfp->mf_trans); mfp->mf_page_size = MEMFILE_PAGE_SIZE; @@ -159,7 +159,7 @@ memfile_T *mf_open(char *fname, int flags) int mf_open_file(memfile_T *mfp, char *fname) { if (mf_do_open(mfp, fname, O_RDWR | O_CREAT | O_EXCL)) { - mfp->mf_dirty = true; + mfp->mf_dirty = MF_DIRTY_YES; return OK; } @@ -269,7 +269,7 @@ bhdr_T *mf_new(memfile_T *mfp, bool negative, unsigned page_count) } } hp->bh_flags = BH_LOCKED | BH_DIRTY; // new block is always dirty - mfp->mf_dirty = true; + mfp->mf_dirty = MF_DIRTY_YES; hp->bh_page_count = page_count; mf_ins_used(mfp, hp); mf_ins_hash(mfp, hp); @@ -342,7 +342,9 @@ void mf_put(memfile_T *mfp, bhdr_T *hp, bool dirty, bool infile) flags &= ~BH_LOCKED; if (dirty) { flags |= BH_DIRTY; - mfp->mf_dirty = true; + if (mfp->mf_dirty != MF_DIRTY_YES_NOSYNC) { + mfp->mf_dirty = MF_DIRTY_YES; + } } hp->bh_flags = flags; if (infile) { @@ -382,8 +384,9 @@ int mf_sync(memfile_T *mfp, int flags) { int got_int_save = got_int; - if (mfp->mf_fd < 0) { // there is no file, nothing to do - mfp->mf_dirty = false; + if (mfp->mf_fd < 0) { + // there is no file, nothing to do + mfp->mf_dirty = MF_DIRTY_NO; return FAIL; } @@ -426,7 +429,7 @@ int mf_sync(memfile_T *mfp, int flags) // If the whole list is flushed, the memfile is not dirty anymore. // In case of an error, dirty flag is also set, to avoid trying all the time. if (hp == NULL || status == FAIL) { - mfp->mf_dirty = false; + mfp->mf_dirty = MF_DIRTY_NO; } if (flags & MFS_FLUSH) { @@ -449,7 +452,7 @@ void mf_set_dirty(memfile_T *mfp) hp->bh_flags |= BH_DIRTY; } } - mfp->mf_dirty = true; + mfp->mf_dirty = MF_DIRTY_YES; } /// Insert block in front of memfile's hash list. diff --git a/src/nvim/memfile_defs.h b/src/nvim/memfile_defs.h index 53152c28f8..917dd6a905 100644 --- a/src/nvim/memfile_defs.h +++ b/src/nvim/memfile_defs.h @@ -38,13 +38,13 @@ typedef struct mf_hashitem { /// mf_hashitem_T which contains the key and linked list pointers. List of items /// in each bucket is doubly-linked. typedef struct mf_hashtab { - size_t mht_mask; /// mask used to mod hash value to array index - /// (nr of items in array is 'mht_mask + 1') - size_t mht_count; /// number of items inserted - mf_hashitem_T **mht_buckets; /// points to the array of buckets (can be - /// mht_small_buckets or a newly allocated array - /// when mht_small_buckets becomes too small) - mf_hashitem_T *mht_small_buckets[MHT_INIT_SIZE]; /// initial buckets + size_t mht_mask; ///< mask used to mod hash value to array index + ///< (nr of items in array is 'mht_mask + 1') + size_t mht_count; ///< number of items inserted + mf_hashitem_T **mht_buckets; ///< points to the array of buckets (can be + ///< mht_small_buckets or a newly allocated array + ///< when mht_small_buckets becomes too small) + mf_hashitem_T *mht_small_buckets[MHT_INIT_SIZE]; ///< initial buckets } mf_hashtab_T; /// A block header. @@ -61,17 +61,17 @@ typedef struct mf_hashtab { /// The blocks in the free list have no block of memory allocated and /// the contents of the block in the file (if any) is irrelevant. typedef struct bhdr { - mf_hashitem_T bh_hashitem; /// header for hash table and key -#define bh_bnum bh_hashitem.mhi_key /// block number, part of bh_hashitem + mf_hashitem_T bh_hashitem; ///< header for hash table and key +#define bh_bnum bh_hashitem.mhi_key ///< block number, part of bh_hashitem - struct bhdr *bh_next; /// next block header in free or used list - struct bhdr *bh_prev; /// previous block header in used list - void *bh_data; /// pointer to memory (for used block) - unsigned bh_page_count; /// number of pages in this block + struct bhdr *bh_next; ///< next block header in free or used list + struct bhdr *bh_prev; ///< previous block header in used list + void *bh_data; ///< pointer to memory (for used block) + unsigned bh_page_count; ///< number of pages in this block #define BH_DIRTY 1U #define BH_LOCKED 2U - unsigned bh_flags; // BH_DIRTY or BH_LOCKED + unsigned bh_flags; ///< BH_DIRTY or BH_LOCKED } bhdr_T; /// A block number translation list item. @@ -81,27 +81,33 @@ typedef struct bhdr { /// number, we remember the translation to the new positive number in the /// double linked trans lists. The structure is the same as the hash lists. typedef struct mf_blocknr_trans_item { - mf_hashitem_T nt_hashitem; /// header for hash table and key -#define nt_old_bnum nt_hashitem.mhi_key /// old, negative, number - blocknr_T nt_new_bnum; /// new, positive, number + mf_hashitem_T nt_hashitem; ///< header for hash table and key +#define nt_old_bnum nt_hashitem.mhi_key ///< old, negative, number + blocknr_T nt_new_bnum; ///< new, positive, number } mf_blocknr_trans_item_T; +typedef enum { + MF_DIRTY_NO = 0, ///< no dirty blocks + MF_DIRTY_YES, ///< there are dirty blocks + MF_DIRTY_YES_NOSYNC, ///< there are dirty blocks, do not sync yet +} mfdirty_T; + /// A memory file. typedef struct memfile { - char *mf_fname; /// name of the file - char *mf_ffname; /// idem, full path - int mf_fd; /// file descriptor - bhdr_T *mf_free_first; /// first block header in free list - bhdr_T *mf_used_first; /// mru block header in used list - bhdr_T *mf_used_last; /// lru block header in used list - mf_hashtab_T mf_hash; /// hash lists - mf_hashtab_T mf_trans; /// trans lists - blocknr_T mf_blocknr_max; /// highest positive block number + 1 - blocknr_T mf_blocknr_min; /// lowest negative block number - 1 - blocknr_T mf_neg_count; /// number of negative blocks numbers - blocknr_T mf_infile_count; /// number of pages in the file - unsigned mf_page_size; /// number of bytes in a page - bool mf_dirty; /// true if there are dirty blocks + char *mf_fname; ///< name of the file + char *mf_ffname; ///< idem, full path + int mf_fd; ///< file descriptor + bhdr_T *mf_free_first; ///< first block header in free list + bhdr_T *mf_used_first; ///< mru block header in used list + bhdr_T *mf_used_last; ///< lru block header in used list + mf_hashtab_T mf_hash; ///< hash lists + mf_hashtab_T mf_trans; ///< trans lists + blocknr_T mf_blocknr_max; ///< highest positive block number + 1 + blocknr_T mf_blocknr_min; ///< lowest negative block number - 1 + blocknr_T mf_neg_count; ///< number of negative blocks numbers + blocknr_T mf_infile_count; ///< number of pages in the file + unsigned mf_page_size; ///< number of bytes in a page + mfdirty_T mf_dirty; } memfile_T; #endif // NVIM_MEMFILE_DEFS_H diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 0815ba411c..eb2afc60b2 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -510,6 +510,8 @@ void ml_open_file(buf_T *buf) continue; } if (mf_open_file(mfp, fname) == OK) { // consumes fname! + // don't sync yet in ml_sync_all() + mfp->mf_dirty = MF_DIRTY_YES_NOSYNC; ml_upd_block0(buf, UB_SAME_DIR); // Flush block zero, so others can read it @@ -1714,7 +1716,7 @@ void ml_sync_all(int check_file, int check_char, bool do_fsync) need_check_timestamps = true; // give message later } } - if (buf->b_ml.ml_mfp->mf_dirty) { + if (buf->b_ml.ml_mfp->mf_dirty == MF_DIRTY_YES) { (void)mf_sync(buf->b_ml.ml_mfp, (check_char ? MFS_STOP : 0) | (do_fsync && bufIsChanged(buf) ? MFS_FLUSH : 0)); if (check_char && os_char_avail()) { // character available now diff --git a/src/nvim/memory.c b/src/nvim/memory.c index 1f550ffb01..6b4d290863 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -816,7 +816,6 @@ void free_all_mem(void) grid_free_all_mem(); clear_hl_tables(false); - list_free_log(); check_quickfix_busy(); diff --git a/src/nvim/ops.c b/src/nvim/ops.c index de77cdd238..c39a3273da 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -854,15 +854,6 @@ static bool is_append_register(int regname) return ASCII_ISUPPER(regname); } -/// @see get_yank_register -/// @returns true when register should be inserted literally -/// (selection or clipboard) -static inline bool is_literal_register(int regname) - FUNC_ATTR_CONST -{ - return regname == '*' || regname == '+'; -} - /// @return a copy of contents in register `name` for use in do_put. Should be /// freed by caller. yankreg_T *copy_register(int name) diff --git a/src/nvim/ops.h b/src/nvim/ops.h index 81e006be27..e378d2f7b5 100644 --- a/src/nvim/ops.h +++ b/src/nvim/ops.h @@ -123,6 +123,15 @@ static inline int op_reg_index(const int regname) } } +/// @see get_yank_register +/// @return true when register should be inserted literally +/// (selection or clipboard) +static inline bool is_literal_register(const int regname) + FUNC_ATTR_CONST +{ + return regname == '*' || regname == '+'; +} + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ops.h.generated.h" #endif diff --git a/src/nvim/os/stdpaths.c b/src/nvim/os/stdpaths.c index 8b62b9e895..53ddda22fa 100644 --- a/src/nvim/os/stdpaths.c +++ b/src/nvim/os/stdpaths.c @@ -69,6 +69,19 @@ const char *get_appname(void) return env_val; } +/// Ensure that APPNAME is valid. In particular, it cannot contain directory separators. +bool appname_is_valid(void) +{ + const char *appname = get_appname(); + const size_t appname_len = strlen(appname); + for (size_t i = 0; i < appname_len; i++) { + if (appname[i] == PATHSEP) { + return false; + } + } + return true; +} + /// Return XDG variable value /// /// @param[in] idx XDG variable to use. diff --git a/src/nvim/po/check.vim b/src/nvim/po/check.vim index 7705ba8577..8752af663b 100644 --- a/src/nvim/po/check.vim +++ b/src/nvim/po/check.vim @@ -6,6 +6,9 @@ if 1 " Only execute this if the eval feature is available. +" using line continuation +set cpo&vim + let filename = "check-" . expand("%:t:r") . ".log" exe 'redir! > ' . filename @@ -62,12 +65,18 @@ while 1 if getline(line('.') - 1) !~ "no-c-format" " go over the "msgid" and "msgid_plural" lines let prevfromline = 'foobar' + let plural = 0 while 1 + if getline('.') =~ 'msgid_plural' + let plural += 1 + endif let fromline = GetMline() if prevfromline != 'foobar' && prevfromline != fromline + \ && (plural != 1 + \ || count(prevfromline, '%') + 1 != count(fromline, '%')) echomsg 'Mismatching % in line ' . (line('.') - 1) echomsg 'msgid: ' . prevfromline - echomsg 'msgid ' . fromline + echomsg 'msgid: ' . fromline if error == 0 let error = line('.') endif @@ -89,6 +98,7 @@ while 1 while getline('.') =~ '^msgstr' let toline = GetMline() if fromline != toline + \ && (plural == 0 || count(fromline, '%') != count(toline, '%') + 1) echomsg 'Mismatching % in line ' . (line('.') - 1) echomsg 'msgid: ' . fromline echomsg 'msgstr: ' . toline diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c index 015c578396..b89d346fbf 100644 --- a/src/nvim/statusline.c +++ b/src/nvim/statusline.c @@ -1031,6 +1031,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n int evaldepth = 0; int curitem = 0; + int foldsignitem = -1; bool prevchar_isflag = true; bool prevchar_isitem = false; @@ -1655,6 +1656,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n if (width == 0) { break; } + foldsignitem = curitem; char *p = NULL; if (fold) { @@ -1664,32 +1666,22 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n p[n] = NUL; } - *buf_tmp = NUL; + size_t buflen = 0; varnumber_T virtnum = get_vim_var_nr(VV_VIRTNUM); - for (int i = 0; i <= width; i++) { - if (i == width) { - if (*buf_tmp == NUL) { - break; - } - stl_items[curitem].minwid = 0; - } else if (!fold) { + for (int i = 0; i < width; i++) { + if (!fold) { SignTextAttrs *sattr = virtnum ? NULL : sign_get_attr(i, stcp->sattrs, wp->w_scwidth); p = sattr && sattr->text ? sattr->text : " "; stl_items[curitem].minwid = -(sattr ? stcp->sign_cul_id ? stcp->sign_cul_id : sattr->hl_id : (stcp->use_cul ? HLF_CLS : HLF_SC) + 1); } - size_t buflen = strlen(buf_tmp); stl_items[curitem].type = Highlight; stl_items[curitem].start = out_p + buflen; + xstrlcpy(buf_tmp + buflen, p, TMPLEN - buflen); + buflen += strlen(p); curitem++; - if (i == width) { - str = buf_tmp; - break; - } - int rc = snprintf(buf_tmp + buflen, sizeof(buf_tmp) - buflen, "%s", p); - (void)rc; // Avoid unused warning on release build - assert(rc > 0); } + str = buf_tmp; break; } @@ -1832,6 +1824,13 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n } } minwid = 0; + // For a 'statuscolumn' sign or fold item, shift the added items + if (foldsignitem >= 0) { + ptrdiff_t offset = out_p - stl_items[foldsignitem].start; + for (int i = foldsignitem; i < curitem; i++) { + stl_items[i].start += offset; + } + } } else { // Note: The negative value denotes a left aligned item. // Here we switch the minimum width back to a positive value. @@ -1851,6 +1850,14 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n } // } + // For a 'statuscolumn' sign or fold item, add an item to reset the highlight group + if (foldsignitem >= 0) { + foldsignitem = -1; + stl_items[curitem].type = Highlight; + stl_items[curitem].start = out_p; + stl_items[curitem].minwid = 0; + } + // For left-aligned items, fill any remaining space with the fillchar for (; l < minwid && out_p < out_end_p; l++) { MB_CHAR2BYTES(fillchar, out_p); diff --git a/src/nvim/testing.c b/src/nvim/testing.c index 25ec8e898a..eea75f3021 100644 --- a/src/nvim/testing.c +++ b/src/nvim/testing.c @@ -775,5 +775,4 @@ void f_test_write_list_log(typval_T *const argvars, typval_T *const rettv, EvalF if (fname == NULL) { return; } - list_write_log(fname); } |