diff options
Diffstat (limited to 'src')
102 files changed, 1713 insertions, 541 deletions
diff --git a/src/nvim/README.md b/src/nvim/README.md index d14ba85546..affc5c79cc 100644 --- a/src/nvim/README.md +++ b/src/nvim/README.md @@ -38,7 +38,7 @@ alternate file (e.g. stderr) use `LOG_CALLSTACK_TO_FILE(FILE*)`. Requires Many log messages have a shared prefix, such as "UI" or "RPC". Use the shell to filter the log, e.g. at DEBUG level you might want to exclude UI messages: - tail -F ~/.local/share/nvim/log | cat -v | stdbuf -o0 grep -v UI | stdbuf -o0 tee -a log + tail -F ~/.cache/nvim/log | cat -v | stdbuf -o0 grep -v UI | stdbuf -o0 tee -a log Build with ASAN --------------- diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 8d82d22040..67f4f92bf6 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -1200,8 +1200,7 @@ static Array extmark_to_array(ExtmarkInfo extmark, bool id, bool add_dict) /// @param ns_id Namespace id from |nvim_create_namespace()| /// @param id Extmark id /// @param opts Optional parameters. Keys: -/// - limit: Maximum number of marks to return -/// - details Whether to include the details dict +/// - details: Whether to include the details dict /// @param[out] err Error details, if any /// @return (row, col) tuple or empty list () if extmark id was absent ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id, @@ -1400,6 +1399,13 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, /// callbacks. The mark will only be used for the current /// redraw cycle, and not be permantently stored in the /// buffer. +/// - right_gravity : boolean that indicates the direction +/// the extmark will be shifted in when new text is inserted +/// (true for right, false for left). defaults to true. +/// - end_right_gravity : boolean that indicates the direction +/// the extmark end position (if it exists) will be shifted +/// in when new text is inserted (true for right, false +/// for left). Defaults to false. /// @param[out] err Error details, if any /// @return Id of the created/updated extmark Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, @@ -1440,6 +1446,10 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, DecorPriority priority = DECOR_PRIORITY_BASE; colnr_T col2 = 0; VirtText virt_text = KV_INITIAL_VALUE; + bool right_gravity = true; + bool end_right_gravity = false; + bool end_gravity_set = false; + for (size_t i = 0; i < opts.size; i++) { String k = opts.items[i].key; Object *v = &opts.items[i].value; @@ -1522,12 +1532,35 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, goto error; } priority = (DecorPriority)v->data.integer; + } else if (strequal("right_gravity", k.data)) { + if (v->type != kObjectTypeBoolean) { + api_set_error(err, kErrorTypeValidation, + "right_gravity must be a boolean"); + goto error; + } + right_gravity = v->data.boolean; + } else if (strequal("end_right_gravity", k.data)) { + if (v->type != kObjectTypeBoolean) { + api_set_error(err, kErrorTypeValidation, + "end_right_gravity must be a boolean"); + goto error; + } + end_right_gravity = v->data.boolean; + end_gravity_set = true; } else { api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data); goto error; } } + // Only error out if they try to set end_right_gravity without + // setting end_col or end_line + if (line2 == -1 && col2 == 0 && end_gravity_set) { + api_set_error(err, kErrorTypeValidation, + "cannot set end_right_gravity " + "without setting end_line or end_col"); + } + if (col2 >= 0) { if (line2 >= 0 && line2 < buf->b_ml.ml_line_count) { len = STRLEN(ml_get_buf(buf, (linenr_T)line2 + 1, false)); @@ -1572,7 +1605,8 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, } id = extmark_set(buf, (uint64_t)ns_id, id, (int)line, (colnr_T)col, - line2, col2, decor, kExtmarkNoUndo); + line2, col2, decor, right_gravity, + end_right_gravity, kExtmarkNoUndo); } return (Integer)id; @@ -1687,7 +1721,7 @@ Integer nvim_buf_add_highlight(Buffer buffer, extmark_set(buf, ns_id, 0, (int)line, (colnr_T)col_start, end_line, (colnr_T)col_end, - decor_hl(hl_id), kExtmarkNoUndo); + decor_hl(hl_id), true, false, kExtmarkNoUndo); return src_id; } @@ -1796,7 +1830,8 @@ Integer nvim_buf_set_virtual_text(Buffer buffer, Decoration *decor = xcalloc(1, sizeof(*decor)); decor->virt_text = virt_text; - extmark_set(buf, ns_id, 0, (int)line, 0, -1, -1, decor, kExtmarkNoUndo); + extmark_set(buf, ns_id, 0, (int)line, 0, -1, -1, decor, true, + false, kExtmarkNoUndo); return src_id; } diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 8f224e8c78..7cee569989 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -1645,6 +1645,43 @@ bool api_object_to_bool(Object obj, const char *what, } } +HlMessage parse_hl_msg(Array chunks, Error *err) +{ + HlMessage hl_msg = KV_INITIAL_VALUE; + for (size_t i = 0; i < chunks.size; i++) { + if (chunks.items[i].type != kObjectTypeArray) { + api_set_error(err, kErrorTypeValidation, "Chunk is not an array"); + goto free_exit; + } + Array chunk = chunks.items[i].data.array; + if (chunk.size == 0 || chunk.size > 2 + || chunk.items[0].type != kObjectTypeString + || (chunk.size == 2 && chunk.items[1].type != kObjectTypeString)) { + api_set_error(err, kErrorTypeValidation, + "Chunk is not an array with one or two strings"); + goto free_exit; + } + + String str = copy_string(chunk.items[0].data.string); + + int attr = 0; + if (chunk.size == 2) { + String hl = chunk.items[1].data.string; + if (hl.size > 0) { + int hl_id = syn_check_group((char_u *)hl.data, (int)hl.size); + attr = hl_id > 0 ? syn_id2attr(hl_id) : 0; + } + } + kv_push(hl_msg, ((HlMessageChunk){ .text = str, .attr = attr })); + } + + return hl_msg; + +free_exit: + clear_hl_msg(&hl_msg); + return hl_msg; +} + const char *describe_ns(NS ns_id) { String name; diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 1e972e01be..09895a2119 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -990,6 +990,48 @@ void nvim_set_option(uint64_t channel_id, String name, Object value, Error *err) set_option_to(channel_id, NULL, SREQ_GLOBAL, name, value, err); } +/// Echo a message. +/// +/// @param chunks A list of [text, hl_group] arrays, each representing a +/// text chunk with specified highlight. `hl_group` element +/// can be omitted for no highlight. +/// @param history if true, add to |message-history|. +/// @param opts Optional parameters. Reserved for future use. +void nvim_echo(Array chunks, Boolean history, Dictionary opts, Error *err) + FUNC_API_SINCE(7) +{ + HlMessage hl_msg = parse_hl_msg(chunks, err); + if (ERROR_SET(err)) { + goto error; + } + + if (opts.size > 0) { + api_set_error(err, kErrorTypeValidation, "opts dict isn't empty"); + goto error; + } + + no_wait_return++; + msg_start(); + msg_clr_eos(); + bool need_clear = false; + for (uint32_t i = 0; i < kv_size(hl_msg); i++) { + HlMessageChunk chunk = kv_A(hl_msg, i); + msg_multiline_attr((const char *)chunk.text.data, chunk.attr, + false, &need_clear); + } + if (history) { + msg_ext_set_kind("echomsg"); + add_hl_msg_hist(hl_msg); + } else { + msg_ext_set_kind("echo"); + } + no_wait_return--; + msg_end(); + +error: + clear_hl_msg(&hl_msg); +} + /// Writes a message to the Vim output buffer. Does not append "\n", the /// message is buffered (won't display) until a linefeed is written. /// diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 42224d0a4f..140a9c6bcb 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -700,11 +700,15 @@ void do_autocmd(char_u *arg_in, int forceit) last_event = (event_T)-1; // for listing the event name last_group = AUGROUP_ERROR; // for listing the group name if (*arg == '*' || *arg == NUL || *arg == '|') { - for (event_T event = (event_T)0; event < (int)NUM_EVENTS; - event = (event_T)(event + 1)) { - if (do_autocmd_event(event, pat, once, nested, cmd, forceit, group) - == FAIL) { - break; + if (!forceit && *cmd != NUL) { + EMSG(_(e_cannot_define_autocommands_for_all_events)); + } else { + for (event_T event = (event_T)0; event < (int)NUM_EVENTS; + event = (event_T)(event + 1)) { + if (do_autocmd_event(event, pat, once, nested, cmd, forceit, group) + == FAIL) { + break; + } } } } else { diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 0134ee838d..7116b25b29 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -3270,7 +3270,7 @@ void maketitle(void) case 6: buf_p = strappend(buf_p, " -"); break; case 5: case 7: buf_p = strappend(buf_p, " -+"); break; - default: assert(false); + default: abort(); } if (curbuf->b_fname != NULL) { @@ -3486,7 +3486,7 @@ int build_stl_str_hl( stl_items = xmalloc(sizeof(stl_item_t) * stl_items_len); stl_groupitems = xmalloc(sizeof(int) * stl_items_len); stl_hltab = xmalloc(sizeof(stl_hlrec_t) * stl_items_len); - stl_tabtab = xmalloc(sizeof(stl_hlrec_t) * stl_items_len); + stl_tabtab = xmalloc(sizeof(StlClickRecord) * stl_items_len); stl_separator_locations = xmalloc(sizeof(int) * stl_items_len); } @@ -4482,22 +4482,15 @@ int build_stl_str_hl( int num_separators = 0; for (int i = 0; i < itemcnt; i++) { if (stl_items[i].type == Separate) { + // Create an array of the start location for each + // separator mark. + stl_separator_locations[num_separators] = i; num_separators++; } } // If we have separated groups, then we deal with it now if (num_separators) { - // Create an array of the start location for each - // separator mark. - int index = 0; - for (int i = 0; i < itemcnt; i++) { - if (stl_items[i].type == Separate) { - stl_separator_locations[index] = i; - index++; - } - } - int standard_spaces = (maxwidth - width) / num_separators; int final_spaces = (maxwidth - width) - standard_spaces * (num_separators - 1); diff --git a/src/nvim/channel.c b/src/nvim/channel.c index 37cbfb968b..5628ede2e6 100644 --- a/src/nvim/channel.c +++ b/src/nvim/channel.c @@ -304,7 +304,8 @@ Channel *channel_job_start(char **argv, CallbackReader on_stdout, bool pty, bool rpc, bool overlapped, bool detach, const char *cwd, uint16_t pty_width, uint16_t pty_height, - char *term_name, char **env, varnumber_T *status_out) + char *term_name, dict_T *env, + varnumber_T *status_out) { assert(cwd == NULL || os_isdir_executable(cwd)); @@ -358,7 +359,9 @@ Channel *channel_job_start(char **argv, CallbackReader on_stdout, if (status) { EMSG3(_(e_jobspawn), os_strerror(status), cmd); xfree(cmd); - os_free_fullenv(proc->env); + if (proc->env) { + tv_dict_free(proc->env); + } if (proc->type == kProcessTypePty) { xfree(chan->stream.pty.term_name); } @@ -367,8 +370,9 @@ Channel *channel_job_start(char **argv, CallbackReader on_stdout, return NULL; } xfree(cmd); - os_free_fullenv(proc->env); - + if (proc->env) { + tv_dict_free(proc->env); + } wstream_init(&proc->in, 0); if (has_out) { diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 3e52b3e3ce..be265e3f27 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -1747,7 +1747,7 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, goto vim_str2nr_dec; } default: { - assert(false); + abort(); } } } else if ((what & (STR2NR_HEX|STR2NR_OCT|STR2NR_BIN)) @@ -1788,7 +1788,7 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, } // Do the string-to-numeric conversion "manually" to avoid sscanf quirks. - assert(false); // Should’ve used goto earlier. + abort(); // Should’ve used goto earlier. #define PARSE_NUMBER(base, cond, conv) \ do { \ while (!STRING_ENDED(ptr) && (cond)) { \ diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c index e6a616c927..f3ee42fab1 100644 --- a/src/nvim/decoration.c +++ b/src/nvim/decoration.c @@ -69,7 +69,7 @@ void bufhl_add_hl_pos_offset(buf_T *buf, } (void)extmark_set(buf, (uint64_t)src_id, 0, (int)lnum-1, hl_start, (int)lnum-1+end_off, hl_end, - decor, kExtmarkNoUndo); + decor, true, false, kExtmarkNoUndo); } } diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 876e53e3cd..100e88e261 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -313,6 +313,11 @@ static void insert_enter(InsertState *s) set_vim_var_string(VV_CHAR, NULL, -1); ins_apply_autocmds(EVENT_INSERTENTER); + // Check for changed highlighting, e.g. for ModeMsg. + if (need_highlight_changed) { + highlight_changed(); + } + // Make sure the cursor didn't move. Do call check_cursor_col() in // case the text was modified. Since Insert mode was not started yet // a call to check_cursor_col() may move the cursor, especially with @@ -8282,8 +8287,9 @@ static bool ins_bs(int c, int mode, int *inserted_space_p) } } while (revins_on || (curwin->w_cursor.col > mincol - && (curwin->w_cursor.lnum != Insstart_orig.lnum - || curwin->w_cursor.col != Insstart_orig.col))); + && (can_bs(BS_NOSTOP) + || (curwin->w_cursor.lnum != Insstart_orig.lnum + || curwin->w_cursor.col != Insstart_orig.col)))); } did_backspace = true; } diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 8a1556320c..a25b140b2f 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -365,7 +365,7 @@ void eval_init(void) eval_msgpack_type_lists[i] = type_list; if (tv_dict_add(msgpack_types_dict, di) == FAIL) { // There must not be duplicate items in this dictionary by definition. - assert(false); + abort(); } } msgpack_types_dict->dv_lock = VAR_FIXED; diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c index 638fef331a..bd4dc87d31 100644 --- a/src/nvim/eval/decode.c +++ b/src/nvim/eval/decode.c @@ -147,7 +147,7 @@ static inline int json_decoder_pop(ValuesStackItem obj, tv_clear(&key.val); if (tv_dict_add(last_container.container.vval.v_dict, obj_di) == FAIL) { - assert(false); + abort(); } obj_di->di_tv = obj.val; } else { @@ -480,7 +480,7 @@ static inline int parse_json_string(const char *const buf, const size_t buf_len, break; } default: { - assert(false); + abort(); } } } else { diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c index 9a9f2e4287..a4d7af7971 100644 --- a/src/nvim/eval/encode.c +++ b/src/nvim/eval/encode.c @@ -174,7 +174,7 @@ static int conv_error(const char *const msg, const MPConvStack *const mpstack, case kMPConvPartial: { switch (v.data.p.stage) { case kMPConvPartialArgs: { - assert(false); + abort(); break; } case kMPConvPartialSelf: { @@ -237,7 +237,7 @@ bool encode_vim_list_to_buf(const list_T *const list, size_t *const ret_len, char *const buf = xmalloc(len); size_t read_bytes; if (encode_read_from_list(&lrstate, buf, len, &read_bytes) != OK) { - assert(false); + abort(); } assert(len == read_bytes); *ret_buf = buf; diff --git a/src/nvim/eval/executor.c b/src/nvim/eval/executor.c index da05ecda43..bbba9d12f2 100644 --- a/src/nvim/eval/executor.c +++ b/src/nvim/eval/executor.c @@ -118,7 +118,7 @@ int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2, return OK; } case VAR_UNKNOWN: { - assert(false); + abort(); } } } diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 8235d74cbb..1a7bbf2d0e 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -1798,7 +1798,7 @@ static void f_environ(typval_T *argvars, typval_T *rettv, FunPtr fptr) os_copy_fullenv(env, env_size); - for (size_t i = 0; i < env_size; i++) { + for (ssize_t i = env_size - 1; i >= 0; i--) { const char * str = env[i]; const char * const end = strchr(str + (str[0] == '=' ? 1 : 0), '='); @@ -1806,6 +1806,12 @@ static void f_environ(typval_T *argvars, typval_T *rettv, FunPtr fptr) ptrdiff_t len = end - str; assert(len > 0); const char * value = str + len + 1; + if (tv_dict_find(rettv->vval.v_dict, str, len) != NULL) { + // Since we're traversing from the end of the env block to the front, any + // duplicate names encountered should be ignored. This preserves the + // semantics of env vars defined later in the env block taking precedence. + continue; + } tv_dict_add_str(rettv->vval.v_dict, str, len, value); @@ -3301,7 +3307,7 @@ static void f_getcwd(typval_T *argvars, typval_T *rettv, FunPtr fptr) } break; case kCdScopeInvalid: // We should never get here - assert(false); + abort(); } if (from) { @@ -4354,7 +4360,7 @@ static void f_haslocaldir(typval_T *argvars, typval_T *rettv, FunPtr fptr) break; case kCdScopeInvalid: // We should never get here - assert(false); + abort(); } } @@ -4875,6 +4881,95 @@ static void f_jobresize(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = 1; } +static const char *ignored_env_vars[] = { +#ifndef WIN32 + "COLUMNS", + "LINES", + "TERMCAP", + "COLORFGBG", +#endif + NULL +}; + +/// According to comments in src/win/process.c of libuv, Windows has a few +/// "essential" environment variables. +static const char *required_env_vars[] = { +#ifdef WIN32 + "HOMEDRIVE", + "HOMEPATH", + "LOGONSERVER", + "PATH", + "SYSTEMDRIVE", + "SYSTEMROOT", + "TEMP", + "USERDOMAIN", + "USERNAME", + "USERPROFILE", + "WINDIR", +#endif + NULL +}; + +static dict_T *create_environment(const dictitem_T *job_env, + const bool clear_env, + const bool pty) +{ + dict_T * env = tv_dict_alloc(); + + if (!clear_env) { + typval_T temp_env = TV_INITIAL_VALUE; + f_environ(NULL, &temp_env, NULL); + tv_dict_extend(env, temp_env.vval.v_dict, "force"); + tv_dict_free(temp_env.vval.v_dict); + + if (pty) { + // These environment variables generally shouldn't be propagated to the + // child process. We're removing them here so the user can still decide + // they want to explicitly set them. + for (size_t i = 0; + i < ARRAY_SIZE(ignored_env_vars) && ignored_env_vars[i]; + i++) { + dictitem_T *dv = tv_dict_find(env, ignored_env_vars[i], -1); + if (dv) { + tv_dict_item_remove(env, dv); + } + } +#ifndef WIN32 + // Set COLORTERM to "truecolor" if termguicolors is set and 256 + // otherwise, but only if it was set in the parent terminal at all + dictitem_T *dv = tv_dict_find(env, S_LEN("COLORTERM")); + if (dv) { + tv_dict_item_remove(env, dv); + tv_dict_add_str(env, S_LEN("COLORTERM"), p_tgc ? "truecolor" : "256"); + } +#endif + } + } + + if (job_env) { + tv_dict_extend(env, job_env->di_tv.vval.v_dict, "force"); + } + + if (pty) { + // Now that the custom environment is configured, we need to ensure certain + // environment variables are present. + for (size_t i = 0; + i < ARRAY_SIZE(required_env_vars) && required_env_vars[i]; + i++) { + size_t len = strlen(required_env_vars[i]); + dictitem_T *dv = tv_dict_find(env, required_env_vars[i], len); + if (!dv) { + const char *env_var = os_getenv(required_env_vars[i]); + if (env_var) { + tv_dict_add_str(env, required_env_vars[i], len, env_var); + } + } + } + } + + return env; +} + // "jobstart()" function static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -4887,7 +4982,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool executable = true; char **argv = tv_to_argv(&argvars[0], NULL, &executable); - char **env = NULL; + dict_T *env = NULL; if (!argv) { rettv->vval.v_number = executable ? 0 : -1; return; // Did error message in tv_to_argv. @@ -4911,6 +5006,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) on_stderr = CALLBACK_READER_INIT; Callback on_exit = CALLBACK_NONE; char *cwd = NULL; + dictitem_T *job_env = NULL; if (argvars[1].v_type == VAR_DICT) { job_opts = argvars[1].vval.v_dict; @@ -4936,7 +5032,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) #endif char *new_cwd = tv_dict_get_string(job_opts, "cwd", false); - if (new_cwd && strlen(new_cwd) > 0) { + if (new_cwd && *new_cwd != NUL) { cwd = new_cwd; // The new cwd must be a directory. if (!os_isdir_executable((const char *)cwd)) { @@ -4945,52 +5041,22 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } } - dictitem_T *job_env = tv_dict_find(job_opts, S_LEN("env")); - if (job_env) { - if (job_env->di_tv.v_type != VAR_DICT) { - EMSG2(_(e_invarg2), "env"); - shell_free_argv(argv); - return; - } - - size_t custom_env_size = (size_t)tv_dict_len(job_env->di_tv.vval.v_dict); - size_t i = 0; - size_t env_size = 0; - - if (clear_env) { - // + 1 for last null entry - env = xmalloc((custom_env_size + 1) * sizeof(*env)); - env_size = 0; - } else { - env_size = os_get_fullenv_size(); - env = xmalloc((custom_env_size + env_size + 1) * sizeof(*env)); - - os_copy_fullenv(env, env_size); - i = env_size; - } - assert(env); // env must be allocated at this point - - TV_DICT_ITER(job_env->di_tv.vval.v_dict, var, { - const char *str = tv_get_string(&var->di_tv); - assert(str); - size_t len = STRLEN(var->di_key) + strlen(str) + strlen("=") + 1; - env[i] = xmalloc(len); - snprintf(env[i], len, "%s=%s", (char *)var->di_key, str); - i++; - }); - - // must be null terminated - env[env_size + custom_env_size] = NULL; + job_env = tv_dict_find(job_opts, S_LEN("env")); + if (job_env && job_env->di_tv.v_type != VAR_DICT) { + EMSG2(_(e_invarg2), "env"); + shell_free_argv(argv); + return; } - if (!common_job_callbacks(job_opts, &on_stdout, &on_stderr, &on_exit)) { shell_free_argv(argv); return; } } + env = create_environment(job_env, clear_env, pty); + uint16_t width = 0, height = 0; char *term_name = NULL; @@ -10518,6 +10584,11 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) Callback on_exit = CALLBACK_NONE; dict_T *job_opts = NULL; const char *cwd = "."; + dict_T *env = NULL; + const bool pty = true; + bool clear_env = false; + dictitem_T *job_env = NULL; + if (argvars[1].v_type == VAR_DICT) { job_opts = argvars[1].vval.v_dict; @@ -10532,17 +10603,31 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } + job_env = tv_dict_find(job_opts, S_LEN("env")); + if (job_env && job_env->di_tv.v_type != VAR_DICT) { + EMSG2(_(e_invarg2), "env"); + shell_free_argv(argv); + return; + } + + clear_env = tv_dict_get_number(job_opts, "clear_env") != 0; + if (!common_job_callbacks(job_opts, &on_stdout, &on_stderr, &on_exit)) { shell_free_argv(argv); return; } } + env = create_environment(job_env, clear_env, pty); + + const bool rpc = false; + const bool overlapped = false; + const bool detach = false; uint16_t term_width = MAX(0, curwin->w_width_inner - win_col_off(curwin)); Channel *chan = channel_job_start(argv, on_stdout, on_stderr, on_exit, - true, false, false, false, cwd, + pty, rpc, overlapped, detach, cwd, term_width, curwin->w_height_inner, - xstrdup("xterm-256color"), NULL, + xstrdup("xterm-256color"), env, &rettv->vval.v_number); if (rettv->vval.v_number <= 0) { return; diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 02d32a4f86..9be487f4fd 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1523,6 +1523,33 @@ varnumber_T tv_dict_get_number(const dict_T *const d, const char *const key) return tv_get_number(&di->di_tv); } +/// Converts a dict to an environment +/// +/// +char **tv_dict_to_env(dict_T *denv) +{ + size_t env_size = (size_t)tv_dict_len(denv); + + size_t i = 0; + char **env = NULL; + + // + 1 for NULL + env = xmalloc((env_size + 1) * sizeof(*env)); + + TV_DICT_ITER(denv, var, { + const char *str = tv_get_string(&var->di_tv); + assert(str); + size_t len = STRLEN(var->di_key) + strlen(str) + strlen("=") + 1; + env[i] = xmalloc(len); + snprintf(env[i], len, "%s=%s", (char *)var->di_key, str); + i++; + }); + + // must be null terminated + env[env_size] = NULL; + return env; +} + /// Get a string item from a dictionary /// /// @param[in] d Dictionary to get item from. @@ -2494,7 +2521,7 @@ void tv_item_lock(typval_T *const tv, const int deep, const bool lock) break; } case VAR_UNKNOWN: { - assert(false); + abort(); } } #undef CHANGE_LOCK @@ -2666,7 +2693,7 @@ bool tv_equal(typval_T *const tv1, typval_T *const tv2, const bool ic, } } - assert(false); + abort(); return false; } @@ -2719,7 +2746,7 @@ bool tv_check_str_or_nr(const typval_T *const tv) return false; } } - assert(false); + abort(); return false; } @@ -2764,7 +2791,7 @@ bool tv_check_num(const typval_T *const tv) return false; } } - assert(false); + abort(); return false; } @@ -2809,7 +2836,7 @@ bool tv_check_str(const typval_T *const tv) return false; } } - assert(false); + abort(); return false; } diff --git a/src/nvim/event/libuv_process.c b/src/nvim/event/libuv_process.c index 13517d3df1..0b1ecb12e2 100644 --- a/src/nvim/event/libuv_process.c +++ b/src/nvim/event/libuv_process.c @@ -41,7 +41,6 @@ int libuv_process_spawn(LibuvProcess *uvproc) #endif uvproc->uvopts.exit_cb = exit_cb; uvproc->uvopts.cwd = proc->cwd; - uvproc->uvopts.env = proc->env; uvproc->uvopts.stdio = uvproc->uvstdio; uvproc->uvopts.stdio_count = 3; uvproc->uvstdio[0].flags = UV_IGNORE; @@ -49,6 +48,12 @@ int libuv_process_spawn(LibuvProcess *uvproc) uvproc->uvstdio[2].flags = UV_IGNORE; uvproc->uv.data = proc; + if (proc->env) { + uvproc->uvopts.env = tv_dict_to_env(proc->env); + } else { + uvproc->uvopts.env = NULL; + } + if (!proc->in.closed) { uvproc->uvstdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE; #ifdef WIN32 @@ -78,6 +83,9 @@ int libuv_process_spawn(LibuvProcess *uvproc) int status; if ((status = uv_spawn(&proc->loop->uv, &uvproc->uv, &uvproc->uvopts))) { ELOG("uv_spawn failed: %s", uv_strerror(status)); + if (uvproc->uvopts.env) { + os_free_fullenv(uvproc->uvopts.env); + } return status; } @@ -97,6 +105,10 @@ static void close_cb(uv_handle_t *handle) if (proc->internal_close_cb) { proc->internal_close_cb(proc); } + LibuvProcess *uvproc = (LibuvProcess *)proc; + if (uvproc->uvopts.env) { + os_free_fullenv(uvproc->uvopts.env); + } } static void exit_cb(uv_process_t *handle, int64_t status, int term_signal) diff --git a/src/nvim/event/process.h b/src/nvim/event/process.h index 84e81238e9..24debdb276 100644 --- a/src/nvim/event/process.h +++ b/src/nvim/event/process.h @@ -4,6 +4,7 @@ #include "nvim/event/loop.h" #include "nvim/event/rstream.h" #include "nvim/event/wstream.h" +#include "nvim/eval/typval.h" typedef enum { kProcessTypeUv, @@ -23,7 +24,7 @@ struct process { uint64_t stopped_time; // process_stop() timestamp const char *cwd; char **argv; - char **env; + dict_T *env; Stream in, out, err; process_exit_cb cb; internal_process_cb internal_exit_cb, internal_close_cb; diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 61e4d634c6..6d26c5f69c 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -790,7 +790,10 @@ void ex_retab(exarg_T *eap) for (col = 0; col < len; col++) { ptr[col] = (col < num_tabs) ? '\t' : ' '; } - ml_replace(lnum, new_line, false); + if (ml_replace(lnum, new_line, false) == OK) { + // "new_line" may have been copied + new_line = curbuf->b_ml.ml_line_ptr; + } if (first_line == 0) { first_line = lnum; } @@ -2413,7 +2416,10 @@ int do_ecmd( (flags & ECMD_HIDE) || curbuf->terminal ? 0 : DOBUF_UNLOAD, false); - the_curwin->w_closing = false; + // Autocommands may have closed the window. + if (win_valid(the_curwin)) { + the_curwin->w_closing = false; + } buf->b_locked--; // autocmds may abort script processing @@ -3126,6 +3132,9 @@ static bool sub_joining_lines(exarg_T *eap, char_u *pat, char_u *sub, || *cmd == 'l' || *cmd == 'p' || *cmd == '#')))) { + if (eap->skip) { + return true; + } curwin->w_cursor.lnum = eap->line1; if (*cmd == 'l') { eap->flags = EXFLAG_LIST; @@ -3307,11 +3316,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, int save_b_changed = curbuf->b_changed; bool preview = (State & CMDPREVIEW); - // inccommand tests fail without this check - if (!preview) { - // Required for Undo to work for extmarks. - u_save_cursor(); - } + bool did_save = false; if (!global_busy) { sub_nsubs = 0; @@ -3693,6 +3698,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, } else { char_u *orig_line = NULL; int len_change = 0; + const bool save_p_lz = p_lz; int save_p_fen = curwin->w_p_fen; curwin->w_p_fen = FALSE; @@ -3701,6 +3707,9 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, int temp = RedrawingDisabled; RedrawingDisabled = 0; + // avoid calling update_screen() in vgetorpeek() + p_lz = false; + if (new_start != NULL) { /* There already was a substitution, we would * like to show this to the user. We cannot @@ -3754,7 +3763,8 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, /* clear the question */ msg_didout = FALSE; /* don't scroll up */ msg_col = 0; - gotocmdline(TRUE); + gotocmdline(true); + p_lz = save_p_lz; // restore the line if (orig_line != NULL) { @@ -3983,6 +3993,11 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, int matchcols = end.col - ((end.lnum == start.lnum) ? start.col : 0); int subcols = new_endcol - ((lnum == lnum_start) ? start_col : 0); + if (!did_save) { + // Required for Undo to work for extmarks. + u_save_cursor(); + did_save = true; + } extmark_splice(curbuf, lnum_start-1, start_col, end.lnum-start.lnum, matchcols, replaced_bytes, lnum-lnum_start, subcols, sublen-1, kExtmarkUndo); diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua index 069d5d461b..e9046da800 100644 --- a/src/nvim/ex_cmds.lua +++ b/src/nvim/ex_cmds.lua @@ -2455,7 +2455,7 @@ module.cmds = { }, { command='sleep', - flags=bit.bor(RANGE, COUNT, EXTRA, TRLBAR, CMDWIN), + flags=bit.bor(BANG, RANGE, COUNT, EXTRA, TRLBAR, CMDWIN), addr_type='ADDR_OTHER', func='ex_sleep', }, diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index c400975108..e49bb99aa0 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -1639,10 +1639,10 @@ int get_arglist_exp(char_u *str, int *fcountp, char_u ***fnamesp, bool wig) if (wig) { i = expand_wildcards(ga.ga_len, (char_u **)ga.ga_data, - fcountp, fnamesp, EW_FILE|EW_NOTFOUND); + fcountp, fnamesp, EW_FILE|EW_NOTFOUND|EW_NOTWILD); } else { i = gen_expand_wildcards(ga.ga_len, (char_u **)ga.ga_data, - fcountp, fnamesp, EW_FILE|EW_NOTFOUND); + fcountp, fnamesp, EW_FILE|EW_NOTFOUND|EW_NOTWILD); } ga_clear(&ga); diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index ccf7dd0f68..52da7fe4f6 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -317,7 +317,9 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, int count = 0; /* line number count */ int did_inc = FALSE; /* incremented RedrawingDisabled */ int retval = OK; - cstack_T cstack; // conditional stack + cstack_T cstack = { // conditional stack + .cs_idx = -1, + }; garray_T lines_ga; // keep lines for ":while"/":for" int current_line = 0; // active line in lines_ga char_u *fname = NULL; // function or script name @@ -360,11 +362,6 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, call_depth++; start_batch_changes(); - cstack.cs_idx = -1; - cstack.cs_looplevel = 0; - cstack.cs_trylevel = 0; - cstack.cs_emsg_silent_list = NULL; - cstack.cs_lflags = 0; ga_init(&lines_ga, (int)sizeof(wcmd_T), 10); real_cookie = getline_cookie(fgetline, cookie); @@ -3041,9 +3038,10 @@ const char * set_one_cmd_context( p = arg + 1; arg = (const char *)skip_cmd_arg((char_u *)arg, false); - /* Still touching the command after '+'? */ - if (*arg == NUL) + // Still touching the command after '+'? + if (*arg == NUL) { return p; + } // Skip space(s) after +command to get to the real argument. arg = (const char *)skipwhite((const char_u *)arg); @@ -3680,6 +3678,10 @@ const char * set_one_cmd_context( xp->xp_pattern = (char_u *)arg; break; + case CMD_lua: + xp->xp_context = EXPAND_LUA; + break; + default: break; } @@ -3968,7 +3970,7 @@ static linenr_T get_address(exarg_T *eap, break; default: - if (ascii_isdigit(*cmd)) { // absolute line number + if (ascii_isdigit(*cmd)) { // absolute line number lnum = getdigits_long(&cmd, false, 0); } } @@ -5187,6 +5189,7 @@ static const char *command_complete[] = #ifdef HAVE_WORKING_LIBINTL [EXPAND_LOCALES] = "locale", #endif + [EXPAND_LUA] = "lua", [EXPAND_MAPCLEAR] = "mapclear", [EXPAND_MAPPINGS] = "mapping", [EXPAND_MENUS] = "menu", @@ -5400,8 +5403,8 @@ static int uc_scan_attr(char_u *attr, size_t len, uint32_t *argt, long *def, size_t vallen = 0; size_t attrlen = len; - /* Look for the attribute name - which is the part before any '=' */ - for (i = 0; i < (int)len; ++i) { + // Look for the attribute name - which is the part before any '=' + for (i = 0; i < (int)len; i++) { if (attr[i] == '=') { val = &attr[i + 1]; vallen = len - i - 1; @@ -7503,8 +7506,9 @@ static void ex_read(exarg_T *eap) } if (*eap->arg == NUL) { - if (check_fname() == FAIL) /* check for no file name */ + if (check_fname() == FAIL) { // check for no file name return; + } i = readfile(curbuf->b_ffname, curbuf->b_fname, eap->line2, (linenr_T)0, (linenr_T)MAXLNUM, eap, 0); } else { @@ -7587,7 +7591,7 @@ void post_chdir(CdScope scope, bool trigger_dirchanged) curwin->w_localdir = (char_u *)xstrdup(cwd); break; case kCdScopeInvalid: - assert(false); + abort(); } shorten_fnames(true); diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 626b840798..cf6bb6d492 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -69,6 +69,7 @@ #include "nvim/lib/kvec.h" #include "nvim/api/private/helpers.h" #include "nvim/highlight_defs.h" +#include "nvim/lua/executor.h" #include "nvim/viml/parser/parser.h" #include "nvim/viml/parser/expressions.h" @@ -3828,7 +3829,7 @@ static void cmd_cursor_goto(int row, int col) ui_grid_cursor_goto(grid->handle, row, col); } -void gotocmdline(int clr) +void gotocmdline(bool clr) { if (ui_has(kUICmdline)) { return; @@ -3945,6 +3946,12 @@ nextwild ( p2 = ExpandOne(xp, p1, vim_strnsave(&ccline.cmdbuff[i], xp->xp_pattern_len), use_options, type); xfree(p1); + + // xp->xp_pattern might have been modified by ExpandOne (for example, + // in lua completion), so recompute the pattern index and length + i = (int)(xp->xp_pattern - ccline.cmdbuff); + xp->xp_pattern_len = (size_t)ccline.cmdpos - (size_t)i; + // Longest match: make sure it is not shorter, happens with :help. if (p2 != NULL && type == WILD_LONGEST) { for (j = 0; (size_t)j < xp->xp_pattern_len; j++) { @@ -3960,7 +3967,7 @@ nextwild ( } if (p2 != NULL && !got_int) { - difflen = (int)STRLEN(p2) - (int)xp->xp_pattern_len; + difflen = (int)STRLEN(p2) - (int)(xp->xp_pattern_len); if (ccline.cmdlen + difflen + 4 > ccline.cmdbufflen) { realloc_cmdbuff(ccline.cmdlen + difflen + 4); xp->xp_pattern = ccline.cmdbuff + i; @@ -5106,6 +5113,10 @@ ExpandFromContext ( if (xp->xp_context == EXPAND_PACKADD) { return ExpandPackAddDir(pat, num_file, file); } + if (xp->xp_context == EXPAND_LUA) { + ILOG("PAT %s", pat); + return nlua_expand_pat(xp, pat, num_file, file); + } regmatch.regprog = vim_regcomp(pat, p_magic ? RE_MAGIC : 0); if (regmatch.regprog == NULL) @@ -6372,7 +6383,7 @@ int hist_type2char(int type) return '>'; } default: { - assert(false); + abort(); } } return NUL; diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c index b2d8532cd7..cacbeddb32 100644 --- a/src/nvim/extmark.c +++ b/src/nvim/extmark.c @@ -71,7 +71,8 @@ static ExtmarkNs *buf_ns_ref(buf_T *buf, uint64_t ns_id, bool put) { /// @returns the mark id uint64_t extmark_set(buf_T *buf, uint64_t ns_id, uint64_t id, int row, colnr_T col, int end_row, colnr_T end_col, - Decoration *decor, ExtmarkOp op) + Decoration *decor, bool right_gravity, + bool end_right_gravity, ExtmarkOp op) { ExtmarkNs *ns = buf_ns_ref(buf, ns_id, true); assert(ns != NULL); @@ -109,10 +110,10 @@ uint64_t extmark_set(buf_T *buf, uint64_t ns_id, uint64_t id, if (end_row > -1) { mark = marktree_put_pair(buf->b_marktree, - row, col, true, - end_row, end_col, false); + row, col, right_gravity, + end_row, end_col, end_right_gravity); } else { - mark = marktree_put(buf->b_marktree, row, col, true); + mark = marktree_put(buf->b_marktree, row, col, right_gravity); } revised: diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c index b1fa0b6779..8beba38509 100644 --- a/src/nvim/file_search.c +++ b/src/nvim/file_search.c @@ -1595,7 +1595,7 @@ void do_autocmd_dirchanged(char *new_dir, CdScope scope, bool changed_window) } case kCdScopeInvalid: { // Should never happen. - assert(false); + abort(); } } diff --git a/src/nvim/fold.c b/src/nvim/fold.c index 0593c16999..5032646d7e 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -2999,7 +2999,6 @@ static void foldlevelDiff(fline_T *flp) static void foldlevelExpr(fline_T *flp) { win_T *win; - int n; int c; linenr_T lnum = flp->lnum + flp->off; @@ -3017,7 +3016,7 @@ static void foldlevelExpr(fline_T *flp) /* KeyTyped may be reset to 0 when calling a function which invokes * do_cmdline(). To make 'foldopen' work correctly restore KeyTyped. */ const bool save_keytyped = KeyTyped; - n = (int)eval_foldexpr(flp->wp->w_p_fde, &c); + const int n = eval_foldexpr(flp->wp->w_p_fde, &c); KeyTyped = save_keytyped; switch (c) { @@ -3202,8 +3201,10 @@ int put_folds(FILE *fd, win_T *wp) { if (foldmethodIsManual(wp)) { if (put_line(fd, "silent! normal! zE") == FAIL - || put_folds_recurse(fd, &wp->w_folds, (linenr_T)0) == FAIL) + || put_folds_recurse(fd, &wp->w_folds, (linenr_T)0) == FAIL + || put_line(fd, "let &fdl = &fdl") == FAIL) { return FAIL; + } } /* If some folds are manually opened/closed, need to restore that. */ diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 3b8f4116b7..22f06941aa 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -1009,6 +1009,8 @@ EXTERN char_u e_floatonly[] INIT(=N_( EXTERN char_u e_floatexchange[] INIT(=N_( "E5602: Cannot exchange or rotate float")); +EXTERN char e_cannot_define_autocommands_for_all_events[] INIT(= N_( + "E1155: Cannot define autocommands for ALL events")); EXTERN char top_bot_msg[] INIT(= N_("search hit TOP, continuing at BOTTOM")); EXTERN char bot_top_msg[] INIT(= N_("search hit BOTTOM, continuing at TOP")); diff --git a/src/nvim/log.c b/src/nvim/log.c index 19203a3c2a..324382a0f7 100644 --- a/src/nvim/log.c +++ b/src/nvim/log.c @@ -51,7 +51,7 @@ static bool log_try_create(char *fname) /// Initializes path to log file. Sets $NVIM_LOG_FILE if empty. /// -/// Tries $NVIM_LOG_FILE, or falls back to $XDG_DATA_HOME/nvim/log. Path to log +/// Tries $NVIM_LOG_FILE, or falls back to $XDG_CACHE_HOME/nvim/log. Path to log /// file is cached, so only the first call has effect, unless first call was not /// successful. Failed initialization indicates either a bug in expand_env() /// or both $NVIM_LOG_FILE and $HOME environment variables are undefined. @@ -69,8 +69,16 @@ static bool log_path_init(void) || log_file_path[0] == '\0' || os_isdir((char_u *)log_file_path) || !log_try_create(log_file_path)) { + // Make kXDGCacheHome if it does not exist. + char *cachehome = get_xdg_home(kXDGCacheHome); + char *failed_dir = NULL; + bool log_dir_failure = false; + if (!os_isdir((char_u *)cachehome)) { + log_dir_failure = (os_mkdir_recurse(cachehome, 0700, &failed_dir) != 0); + } + XFREE_CLEAR(cachehome); // Invalid $NVIM_LOG_FILE or failed to expand; fall back to default. - char *defaultpath = stdpaths_user_data_subpath("log", 0, true); + char *defaultpath = stdpaths_user_cache_subpath("log"); size_t len = xstrlcpy(log_file_path, defaultpath, size); xfree(defaultpath); // Fall back to .nvimlog @@ -83,6 +91,11 @@ static bool log_path_init(void) return false; } os_setenv(LOG_FILE_ENV, log_file_path, true); + if (log_dir_failure) { + WLOG("Failed to create directory %s for writing logs: %s", + failed_dir, os_strerror(log_dir_failure)); + } + XFREE_CLEAR(failed_dir); } return true; } @@ -323,4 +336,3 @@ static bool v_do_log_to_file(FILE *log_file, int log_level, return true; } - diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c index 030df69caa..83b3729ad3 100644 --- a/src/nvim/lua/converter.c +++ b/src/nvim/lua/converter.c @@ -245,7 +245,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) } else { dictitem_T *const di = tv_dict_item_alloc_len(s, len); if (tv_dict_add(cur.tv->vval.v_dict, di) == FAIL) { - assert(false); + abort(); } kv_push(stack, cur); cur = (TVPopStackItem) { &di->di_tv, false, false, 0 }; @@ -391,7 +391,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) break; } default: { - assert(false); + abort(); } } nlua_pop_typval_table_processing_end: @@ -1200,7 +1200,7 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err) break; } default: { - assert(false); + abort(); } } break; diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 344a2387d6..310b194c8c 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -5,6 +5,7 @@ #include <lualib.h> #include <lauxlib.h> +#include "nvim/version.h" #include "nvim/misc1.h" #include "nvim/getchar.h" #include "nvim/garray.h" @@ -78,6 +79,17 @@ static void nlua_error(lua_State *const lstate, const char *const msg) lua_pop(lstate, 1); } +/// Return version of current neovim build +/// +/// @param lstate Lua interpreter state. +static int nlua_nvim_version(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL +{ + Dictionary version = version_dict(); + nlua_push_Dictionary(lstate, version, true); + api_free_dictionary(version); + return 1; +} + /// Compare two strings, ignoring case /// /// Expects two values on the stack: compared strings. Returns one of the @@ -420,6 +432,9 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL // str_byteindex lua_pushcfunction(lstate, &nlua_str_byteindex); lua_setfield(lstate, -2, "str_byteindex"); + // neovim version + lua_pushcfunction(lstate, &nlua_nvim_version); + lua_setfield(lstate, -2, "version"); // schedule lua_pushcfunction(lstate, &nlua_schedule); lua_setfield(lstate, -2, "schedule"); @@ -1277,6 +1292,80 @@ static void nlua_add_treesitter(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL lua_setfield(lstate, -2, "_ts_parse_query"); } +int nlua_expand_pat(expand_T *xp, + char_u *pat, + int *num_results, + char_u ***results) +{ + lua_State *const lstate = nlua_enter(); + int ret = OK; + + // [ vim ] + lua_getglobal(lstate, "vim"); + + // [ vim, vim._expand_pat ] + lua_getfield(lstate, -1, "_expand_pat"); + luaL_checktype(lstate, -1, LUA_TFUNCTION); + + // [ vim, vim._log_keystroke, buf ] + lua_pushlstring(lstate, (const char *)pat, STRLEN(pat)); + + if (lua_pcall(lstate, 1, 2, 0) != 0) { + nlua_error( + lstate, + _("Error executing vim._expand_pat: %.*s")); + return FAIL; + } + + Error err = ERROR_INIT; + + *num_results = 0; + *results = NULL; + + int prefix_len = (int)nlua_pop_Integer(lstate, &err); + if (ERROR_SET(&err)) { + ret = FAIL; + goto cleanup; + } + + Array completions = nlua_pop_Array(lstate, &err); + if (ERROR_SET(&err)) { + ret = FAIL; + goto cleanup_array; + } + + garray_T result_array; + ga_init(&result_array, (int)sizeof(char *), 80); + for (size_t i = 0; i < completions.size; i++) { + Object v = completions.items[i]; + + if (v.type != kObjectTypeString) { + ret = FAIL; + goto cleanup_array; + } + + GA_APPEND( + char_u *, + &result_array, + vim_strsave((char_u *)v.data.string.data)); + } + + xp->xp_pattern += prefix_len; + *results = result_array.ga_data; + *num_results = result_array.ga_len; + +cleanup_array: + api_free_array(completions); + +cleanup: + + if (ret == FAIL) { + ga_clear(&result_array); + } + + return ret; +} + static int nlua_regex(lua_State *lstate) { Error err = ERROR_INIT; diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua index b20fbbf038..dbf4f6014c 100644 --- a/src/nvim/lua/vim.lua +++ b/src/nvim/lua/vim.lua @@ -121,10 +121,17 @@ function vim._load_package(name) end for _,trail in ipairs(vim._so_trails) do - local path = "lua/"..trail:gsub('?',basename) + local path = "lua"..trail:gsub('?', basename) -- so_trails contains a leading slash local found = vim.api.nvim_get_runtime_file(path, false) if #found > 0 then - local f, err = package.loadlib(found[1]) + -- Making function name in Lua 5.1 (see src/loadlib.c:mkfuncname) is + -- a) strip prefix up to and including the first dash, if any + -- b) replace all dots by underscores + -- c) prepend "luaopen_" + -- So "foo-bar.baz" should result in "luaopen_bar_baz" + local dash = name:find("-", 1, true) + local modname = dash and name:sub(dash + 1) or name + local f, err = package.loadlib(found[1], "luaopen_"..modname:gsub("%.", "_")) return f or error(err) end end @@ -527,4 +534,164 @@ function vim._log_keystroke(char) end end +--- Generate a list of possible completions for the string. +--- String starts with ^ and then has the pattern. +--- +--- 1. Can we get it to just return things in the global namespace with that name prefix +--- 2. Can we get it to return things from global namespace even with `print(` in front. +function vim._expand_pat(pat, env) + env = env or _G + + pat = string.sub(pat, 2, #pat) + + if pat == '' then + local result = vim.tbl_keys(env) + table.sort(result) + return result, 0 + end + + -- TODO: We can handle spaces in [] ONLY. + -- We should probably do that at some point, just for cooler completion. + -- TODO: We can suggest the variable names to go in [] + -- This would be difficult as well. + -- Probably just need to do a smarter match than just `:match` + + -- Get the last part of the pattern + local last_part = pat:match("[%w.:_%[%]'\"]+$") + if not last_part then return {}, 0 end + + local parts, search_index = vim._expand_pat_get_parts(last_part) + + local match_part = string.sub(last_part, search_index, #last_part) + local prefix_match_pat = string.sub(pat, 1, #pat - #match_part) or '' + + local final_env = env + + for _, part in ipairs(parts) do + if type(final_env) ~= 'table' then + return {}, 0 + end + local key + + -- Normally, we just have a string + -- Just attempt to get the string directly from the environment + if type(part) == "string" then + key = part + else + -- However, sometimes you want to use a variable, and complete on it + -- With this, you have the power. + + -- MY_VAR = "api" + -- vim[MY_VAR] + -- -> _G[MY_VAR] -> "api" + local result_key = part[1] + if not result_key then + return {}, 0 + end + + local result = rawget(env, result_key) + + if result == nil then + return {}, 0 + end + + key = result + end + local field = rawget(final_env, key) + if field == nil then + local mt = getmetatable(final_env) + if mt and type(mt.__index) == "table" then + field = rawget(mt.__index, key) + end + end + final_env = field + + if not final_env then + return {}, 0 + end + end + + local keys = {} + local function insert_keys(obj) + for k,_ in pairs(obj) do + if type(k) == "string" and string.sub(k,1,string.len(match_part)) == match_part then + table.insert(keys,k) + end + end + end + + if type(final_env) == "table" then + insert_keys(final_env) + end + local mt = getmetatable(final_env) + if mt and type(mt.__index) == "table" then + insert_keys(mt.__index) + end + + table.sort(keys) + + return keys, #prefix_match_pat +end + +vim._expand_pat_get_parts = function(lua_string) + local parts = {} + + local accumulator, search_index = '', 1 + local in_brackets, bracket_end = false, -1 + local string_char = nil + for idx = 1, #lua_string do + local s = lua_string:sub(idx, idx) + + if not in_brackets and (s == "." or s == ":") then + table.insert(parts, accumulator) + accumulator = '' + + search_index = idx + 1 + elseif s == "[" then + in_brackets = true + + table.insert(parts, accumulator) + accumulator = '' + + search_index = idx + 1 + elseif in_brackets then + if idx == bracket_end then + in_brackets = false + search_index = idx + 1 + + if string_char == "VAR" then + table.insert(parts, { accumulator }) + accumulator = '' + + string_char = nil + end + elseif not string_char then + bracket_end = string.find(lua_string, ']', idx, true) + + if s == '"' or s == "'" then + string_char = s + elseif s ~= ' ' then + string_char = "VAR" + accumulator = s + end + elseif string_char then + if string_char ~= s then + accumulator = accumulator .. s + else + table.insert(parts, accumulator) + accumulator = '' + + string_char = nil + end + end + else + accumulator = accumulator .. s + end + end + + parts = vim.tbl_filter(function(val) return #val > 0 end, parts) + + return parts, search_index +end + return module diff --git a/src/nvim/main.c b/src/nvim/main.c index 8bf745966e..9f71df3a46 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -2044,7 +2044,6 @@ static void usage(void) mch_msg(_(" -u <config> Use this config file\n")); mch_msg(_(" -v, --version Print version information\n")); mch_msg(_(" -V[N][file] Verbose [level][file]\n")); - mch_msg(_(" -Z Restricted mode\n")); mch_msg("\n"); mch_msg(_(" --api-info Write msgpack-encoded API metadata to stdout\n")); mch_msg(_(" --embed Use stdin/stdout as a msgpack-rpc channel\n")); diff --git a/src/nvim/mark.c b/src/nvim/mark.c index 45ca097033..73a9c1d1d7 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -777,6 +777,7 @@ void ex_delmarks(exarg_T *eap) n = i - 'A'; } namedfm[n].fmark.mark.lnum = 0; + namedfm[n].fmark.fnum = 0; XFREE_CLEAR(namedfm[n].fname); } } diff --git a/src/nvim/message.c b/src/nvim/message.c index f94529c687..ba7a667a60 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -890,6 +890,40 @@ char_u *msg_may_trunc(int force, char_u *s) return s; } +void clear_hl_msg(HlMessage *hl_msg) +{ + for (size_t i = 0; i < kv_size(*hl_msg); i++) { + xfree(kv_A(*hl_msg, i).text.data); + } + kv_destroy(*hl_msg); + *hl_msg = (HlMessage)KV_INITIAL_VALUE; +} + +#define LINE_BUFFER_SIZE 4096 + +void add_hl_msg_hist(HlMessage hl_msg) +{ + // TODO(notomo): support multi highlighted message history + size_t pos = 0; + char buf[LINE_BUFFER_SIZE]; + for (uint32_t i = 0; i < kv_size(hl_msg); i++) { + HlMessageChunk chunk = kv_A(hl_msg, i); + for (uint32_t j = 0; j < chunk.text.size; j++) { + if (pos == LINE_BUFFER_SIZE - 1) { + buf[pos] = NUL; + add_msg_hist((const char *)buf, -1, MSG_HIST, true); + pos = 0; + continue; + } + buf[pos++] = chunk.text.data[j]; + } + } + if (pos != 0) { + buf[pos] = NUL; + add_msg_hist((const char *)buf, -1, MSG_HIST, true); + } +} + /// @param[in] len Length of s or -1. static void add_msg_hist(const char *s, int len, int attr, bool multiline) { diff --git a/src/nvim/message.h b/src/nvim/message.h index fdb9bc96ca..377c725fa1 100644 --- a/src/nvim/message.h +++ b/src/nvim/message.h @@ -8,6 +8,8 @@ #include "nvim/macros.h" #include "nvim/types.h" #include "nvim/grid_defs.h" +#include "nvim/api/private/defs.h" +#include "nvim/lib/kvec.h" /* * Types of dialogs passed to do_dialog(). @@ -75,6 +77,13 @@ /// Like #MSG_PUTS_ATTR, but if middle part of long messages will be replaced #define MSG_PUTS_LONG_ATTR(s, a) msg_puts_long_attr((char_u *)(s), (a)) +typedef struct { + String text; + int attr; +} HlMessageChunk; + +typedef kvec_t(HlMessageChunk) HlMessage; + /// Message history for `:messages` typedef struct msg_hist { struct msg_hist *next; ///< Next message. diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 4e955667dc..8f22243348 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -3978,16 +3978,19 @@ static bool nv_screengo(oparg_T *oap, int dir, long dist) curwin->w_curswant -= width2; } else { // to previous line + + // Move to the start of a closed fold. Don't do that when + // 'foldopen' contains "all": it will open in a moment. + if (!(fdo_flags & FDO_ALL)) { + (void)hasFolding(curwin->w_cursor.lnum, + &curwin->w_cursor.lnum, NULL); + } if (curwin->w_cursor.lnum == 1) { retval = false; break; } - --curwin->w_cursor.lnum; - /* Move to the start of a closed fold. Don't do that when - * 'foldopen' contains "all": it will open in a moment. */ - if (!(fdo_flags & FDO_ALL)) - (void)hasFolding(curwin->w_cursor.lnum, - &curwin->w_cursor.lnum, NULL); + curwin->w_cursor.lnum--; + linelen = linetabsize(get_cursor_line_ptr()); if (linelen > width1) { int w = (((linelen - width1 - 1) / width2) + 1) * width2; @@ -6708,11 +6711,8 @@ static void nv_g_cmd(cmdarg_T *cap) */ case 'j': case K_DOWN: - /* with 'nowrap' it works just like the normal "j" command; also when - * in a closed fold */ - if (!curwin->w_p_wrap - || hasFolding(curwin->w_cursor.lnum, NULL, NULL) - ) { + // with 'nowrap' it works just like the normal "j" command. + if (!curwin->w_p_wrap) { oap->motion_type = kMTLineWise; i = cursor_down(cap->count1, oap->op_type == OP_NOP); } else @@ -6723,11 +6723,8 @@ static void nv_g_cmd(cmdarg_T *cap) case 'k': case K_UP: - /* with 'nowrap' it works just like the normal "k" command; also when - * in a closed fold */ - if (!curwin->w_p_wrap - || hasFolding(curwin->w_cursor.lnum, NULL, NULL) - ) { + // with 'nowrap' it works just like the normal "k" command. + if (!curwin->w_p_wrap) { oap->motion_type = kMTLineWise; i = cursor_up(cap->count1, oap->op_type == OP_NOP); } else diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 052b07ed44..87d092281a 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -2623,7 +2623,7 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) } // NOTREACHED case kMTUnknown: - assert(false); + abort(); } } @@ -6092,7 +6092,7 @@ static void set_clipboard(int name, yankreg_T *reg) break; } case kMTUnknown: { - assert(false); + abort(); } } diff --git a/src/nvim/option.c b/src/nvim/option.c index d43dd9ba15..7cc83399e5 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -306,7 +306,7 @@ static char *(p_buftype_values[]) = { "nofile", "nowrite", "quickfix", static char *(p_bufhidden_values[]) = { "hide", "unload", "delete", "wipe", NULL }; -static char *(p_bs_values[]) = { "indent", "eol", "start", NULL }; +static char *(p_bs_values[]) = { "indent", "eol", "start", "nostop", NULL }; static char *(p_fdm_values[]) = { "manual", "expr", "marker", "indent", "syntax", "diff", NULL }; static char *(p_fcl_values[]) = { "all", NULL }; @@ -1366,6 +1366,10 @@ int do_set( *(char_u **)varp = vim_strsave( (char_u *)"indent,eol,start"); break; + case 3: + *(char_u **)varp = vim_strsave( + (char_u *)"indent,eol,nostop"); + break; } xfree(oldval); if (origval == oldval) { @@ -1938,6 +1942,7 @@ static void didset_options(void) (void)opt_strings_flags(p_dy, p_dy_values, &dy_flags, true); (void)opt_strings_flags(p_rdb, p_rdb_values, &rdb_flags, true); (void)opt_strings_flags(p_tc, p_tc_values, &tc_flags, false); + (void)opt_strings_flags(p_tpf, p_tpf_values, &tpf_flags, true); (void)opt_strings_flags(p_ve, p_ve_values, &ve_flags, true); (void)opt_strings_flags(p_wop, p_wop_values, &wop_flags, true); (void)opt_strings_flags(p_jop, p_jop_values, &jop_flags, true); @@ -2909,7 +2914,7 @@ ambw_end: #endif } else if (varp == &curwin->w_p_scl) { // 'signcolumn' - if (check_opt_strings(*varp, p_scl_values, false) != OK) { + if (check_signcolumn(*varp) != OK) { errmsg = e_invarg; } // When changing the 'signcolumn' to or from 'number', recompute the @@ -2939,7 +2944,7 @@ ambw_end: } } else if (varp == &p_bs) { // 'backspace' if (ascii_isdigit(*p_bs)) { - if (*p_bs >'2' || p_bs[1] != NUL) { + if (*p_bs > '3' || p_bs[1] != NUL) { errmsg = e_invarg; } } else if (check_opt_strings(p_bs, p_bs_values, true) != OK) { @@ -3073,6 +3078,10 @@ ambw_end: if (!parse_winhl_opt(curwin)) { errmsg = e_invarg; } + } else if (varp == &p_tpf) { + if (opt_strings_flags(p_tpf, p_tpf_values, &tpf_flags, true) != OK) { + errmsg = e_invarg; + } } else { // Options that are a list of flags. p = NULL; @@ -3228,6 +3237,34 @@ static int int_cmp(const void *a, const void *b) return *(const int *)a - *(const int *)b; } +/// Handle setting 'signcolumn' for value 'val' +/// +/// @return OK when the value is valid, FAIL otherwise +int check_signcolumn(char_u *val) +{ + // check for basic match + if (check_opt_strings(val, p_scl_values, false) == OK) { + return OK; + } + + // check for 'auto:<NUMBER>-<NUMBER>' + if (STRLEN(val) == 8 + && !STRNCMP(val, "auto:", 5) + && ascii_isdigit(val[5]) + && val[6] == '-' + && ascii_isdigit(val[7]) + ) { + int min = val[5] - '0'; + int max = val[7] - '0'; + if (min < 1 || max < 2 || min > 8 || max > 9 || min >= max) { + return FAIL; + } + return OK; + } + + return FAIL; +} + /// Handle setting 'colorcolumn' or 'textwidth' in window "wp". /// /// @return error message, NULL if it's OK. @@ -6801,15 +6838,15 @@ static int check_opt_wim(void) } /// Check if backspacing over something is allowed. -/// The parameter what is one of the following: whatBS_INDENT, BS_EOL -/// or BS_START +/// @param what BS_INDENT, BS_EOL, BS_START, or BS_NOSTOP bool can_bs(int what) { if (what == BS_START && bt_prompt(curbuf)) { return false; } switch (*p_bs) { - case '2': return true; + case '3': return true; + case '2': return what != BS_NOSTOP; case '1': return what != BS_START; case '0': return false; } @@ -7091,7 +7128,7 @@ int csh_like_shell(void) /// buffer signs and on user configuration. int win_signcol_count(win_T *wp) { - int maximum = 1, needed_signcols; + int minimum = 0, maximum = 1, needed_signcols; const char *scl = (const char *)wp->w_p_scl; // Note: It checks "no" or "number" in 'signcolumn' option @@ -7115,9 +7152,14 @@ int win_signcol_count(win_T *wp) if (!strncmp(scl, "auto:", 5)) { // Variable depending on a configuration maximum = scl[5] - '0'; + // auto:<NUM>-<NUM> + if (strlen(scl) == 8 && *(scl + 6) == '-') { + minimum = maximum; + maximum = scl[7] - '0'; + } } - return MIN(maximum, needed_signcols); + return MAX(minimum, MIN(maximum, needed_signcols)); } /// Get window or buffer local options diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index ec2160d365..43b0107800 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -282,9 +282,14 @@ enum { #define WIM_BUFLASTUSED 8 // arguments for can_bs() +// each defined char should be unique over all values +// except for BS_START, that intentionally also matches BS_NOSTOP +// because BS_NOSTOP behaves exactly the same except it +// does not stop at the start of the insert point #define BS_INDENT 'i' // "Indent" -#define BS_EOL 'o' // "eOl" +#define BS_EOL 'l' // "eoL" #define BS_START 's' // "Start" +#define BS_NOSTOP 'p' // "nostoP #define LISPWORD_VALUE \ "defun,define,defmacro,set!,lambda,if,case,let,flet,let*,letrec,do,do*,define-syntax,let-syntax,letrec-syntax,destructuring-bind,defpackage,defparameter,defstruct,deftype,defvar,do-all-symbols,do-external-symbols,do-symbols,dolist,dotimes,ecase,etypecase,eval-when,labels,macrolet,multiple-value-bind,multiple-value-call,multiple-value-prog1,multiple-value-setq,prog1,progv,typecase,unless,unwind-protect,when,with-input-from-string,with-open-file,with-open-stream,with-output-to-string,with-package-iterator,define-condition,handler-bind,handler-case,restart-bind,restart-case,with-simple-restart,store-value,use-value,muffle-warning,abort,continue,with-slots,with-slots*,with-accessors,with-accessors*,defclass,defmethod,print-unreadable-object" @@ -616,6 +621,19 @@ EXTERN int p_sta; // 'smarttab' EXTERN int p_sb; // 'splitbelow' EXTERN long p_tpm; // 'tabpagemax' EXTERN char_u *p_tal; // 'tabline' +EXTERN char_u *p_tpf; // 'termpastefilter' +EXTERN unsigned int tpf_flags; ///< flags from 'termpastefilter' +#ifdef IN_OPTION_C +static char *(p_tpf_values[]) = + { "BS", "HT", "FF", "ESC", "DEL", "C0", "C1", NULL }; +#endif +# define TPF_BS 0x001 +# define TPF_HT 0x002 +# define TPF_FF 0x004 +# define TPF_ESC 0x008 +# define TPF_DEL 0x010 +# define TPF_C0 0x020 +# define TPF_C1 0x040 EXTERN char_u *p_sps; // 'spellsuggest' EXTERN int p_spr; // 'splitright' EXTERN int p_sol; // 'startofline' diff --git a/src/nvim/options.lua b/src/nvim/options.lua index df2bfbce34..fe108ef1cc 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -2821,6 +2821,14 @@ return { defaults={if_true={vi=false}} }, { + full_name='termpastefilter', abbreviation='tpf', + type='string', list='onecomma', scope={'global'}, + deny_duplicates=true, + vim=true, + varname='p_tpf', + defaults={if_true={vi="", vim="BS,HT,ESC,DEL"}} + }, + { full_name='terse', short_desc=N_("hides notification of search wrap"), type='bool', scope={'global'}, diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index 879266e3d4..008f5ef63b 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -394,13 +394,21 @@ void os_get_hostname(char *hostname, size_t size) } /// To get the "real" home directory: -/// - get value of $HOME +/// 1. get value of $HOME +/// 2. if $HOME is not set, try the following +/// For Windows: +/// 1. assemble homedir using HOMEDRIVE and HOMEPATH +/// 2. try os_homedir() +/// 3. resolve a direct reference to another system variable +/// 4. guess C drive /// For Unix: -/// - go to that directory -/// - do os_dirname() to get the real name of that directory. -/// This also works with mounts and links. -/// Don't do this for Windows, it will change the "current dir" for a drive. +/// 1. try os_homedir() +/// 2. go to that directory +/// This also works with mounts and links. +/// Don't do this for Windows, it will change the "current dir" for a drive. +/// 3. fall back to current working directory as a last resort static char *homedir = NULL; +static char *os_homedir(void); void init_homedir(void) { @@ -430,7 +438,7 @@ void init_homedir(void) } } if (var == NULL) { - var = os_getenv("USERPROFILE"); + var = os_homedir(); } // Weird but true: $HOME may contain an indirect reference to another @@ -440,6 +448,7 @@ void init_homedir(void) const char *p = strchr(var + 1, '%'); if (p != NULL) { vim_snprintf(os_buf, (size_t)(p - var), "%s", var + 1); + var = NULL; const char *exp = os_getenv(os_buf); if (exp != NULL && *exp != NUL && STRLEN(exp) + STRLEN(p) < MAXPATHL) { @@ -458,8 +467,12 @@ void init_homedir(void) } #endif - if (var != NULL) { #ifdef UNIX + if (var == NULL) { + var = os_homedir(); + } + + if (var != NULL) { // Change to the directory and get the actual path. This resolves // links. Don't do it when we can't return. if (os_dirname((char_u *)os_buf, MAXPATHL) == OK && os_chdir(os_buf) == 0) { @@ -470,11 +483,37 @@ void init_homedir(void) EMSG(_(e_prev_dir)); } } + } + + // Fall back to current working directory if home is not found + if ((var == NULL || *var == NUL) + && os_dirname((char_u *)os_buf, sizeof(os_buf)) == OK) { + var = os_buf; + } #endif + if (var != NULL) { homedir = xstrdup(var); } } +static char homedir_buf[MAXPATHL]; + +static char *os_homedir(void) +{ + homedir_buf[0] = NUL; + size_t homedir_size = MAXPATHL; + uv_mutex_lock(&mutex); + // http://docs.libuv.org/en/v1.x/misc.html#c.uv_os_homedir + int ret_value = uv_os_homedir(homedir_buf, &homedir_size); + uv_mutex_unlock(&mutex); + if (ret_value == 0 && homedir_size < MAXPATHL) { + return homedir_buf; + } + ELOG("uv_os_homedir() failed %d: %s", ret_value, os_strerror(ret_value)); + homedir_buf[0] = NUL; + return NULL; +} + #if defined(EXITFREE) void free_homedir(void) diff --git a/src/nvim/os/pty_process_unix.c b/src/nvim/os/pty_process_unix.c index 4d7d9a45df..348a139e79 100644 --- a/src/nvim/os/pty_process_unix.c +++ b/src/nvim/os/pty_process_unix.c @@ -20,6 +20,10 @@ # include <pty.h> #endif +#ifdef __APPLE__ +# include <crt_externs.h> +#endif + #include <uv.h> #include "nvim/lib/klist.h" @@ -154,28 +158,14 @@ void pty_process_teardown(Loop *loop) static void init_child(PtyProcess *ptyproc) FUNC_ATTR_NONNULL_ALL { +#if defined(HAVE__NSGETENVIRON) +#define environ (*_NSGetEnviron()) +#else + extern char **environ; +#endif // New session/process-group. #6530 setsid(); - os_unsetenv("COLUMNS"); - os_unsetenv("LINES"); - os_unsetenv("TERMCAP"); - os_unsetenv("COLORFGBG"); - // setting COLORTERM to "truecolor" if termguicolors is set and 256 - // otherwise, but only if it was set in the parent terminal at all - if (os_env_exists("COLORTERM")) { - const char *colorterm = os_getenv("COLORTERM"); - if (colorterm != NULL) { - if (p_tgc) { - os_setenv("COLORTERM", "truecolor", 1); - } else { - os_setenv("COLORTERM", "256", 1); - } - } else { - os_unsetenv("COLORTERM"); - } - } - signal(SIGCHLD, SIG_DFL); signal(SIGHUP, SIG_DFL); signal(SIGINT, SIG_DFL); @@ -190,9 +180,14 @@ static void init_child(PtyProcess *ptyproc) } char *prog = ptyproc->process.argv[0]; - os_setenv("TERM", ptyproc->term_name ? ptyproc->term_name : "ansi", 1); - execvp(prog, ptyproc->process.argv); + + assert(proc->env); + tv_dict_add_str(proc->env, S_LEN("TERM"), + ptyproc->term_name ? ptyproc->term_name : "ansi"); + environ = tv_dict_to_env(proc->env); + execvp(prog, proc->argv); ELOG("execvp failed: %s: %s", strerror(errno), prog); + _exit(122); // 122 is EXEC_FAILED in the Vim source. } diff --git a/src/nvim/os/pty_process_win.c b/src/nvim/os/pty_process_win.c index 6f7100e846..52d2f84ace 100644 --- a/src/nvim/os/pty_process_win.c +++ b/src/nvim/os/pty_process_win.c @@ -52,6 +52,7 @@ int pty_process_spawn(PtyProcess *ptyproc) uv_connect_t *out_req = NULL; wchar_t *cmd_line = NULL; wchar_t *cwd = NULL; + wchar_t *env = NULL; const char *emsg = NULL; assert(proc->err.closed); @@ -124,13 +125,22 @@ int pty_process_spawn(PtyProcess *ptyproc) goto cleanup; } + if (proc->env != NULL) { + status = build_env_block(proc->env, &env); + } + + if (status != 0) { + emsg = "build_env_block failed"; + goto cleanup; + } + if (ptyproc->type == kConpty) { if (!os_conpty_spawn(conpty_object, &process_handle, NULL, cmd_line, cwd, - NULL)) { + env)) { emsg = "os_conpty_spawn failed"; status = (int)GetLastError(); goto cleanup; @@ -141,7 +151,7 @@ int pty_process_spawn(PtyProcess *ptyproc) NULL, // Optional application name cmd_line, cwd, - NULL, // Optional environment variables + env, &err); if (spawncfg == NULL) { emsg = "winpty_spawn_config_new failed"; @@ -213,6 +223,7 @@ cleanup: xfree(in_req); xfree(out_req); xfree(cmd_line); + xfree(env); xfree(cwd); return status; } @@ -454,3 +465,66 @@ int translate_winpty_error(int winpty_errno) default: return UV_UNKNOWN; } } + +typedef struct EnvNode { + wchar_t *str; + size_t len; + QUEUE node; +} EnvNode; + +/// Build the environment block to pass to CreateProcessW. +/// +/// @param[in] denv Dict of environment name/value pairs +/// @param[out] env Allocated environment block +/// +/// @returns zero on success or error code of MultiByteToWideChar function. +static int build_env_block(dict_T *denv, wchar_t **env_block) +{ + const size_t denv_size = (size_t)tv_dict_len(denv); + size_t env_block_len = 0; + int rc; + char **env = tv_dict_to_env(denv); + + QUEUE *q; + QUEUE env_q; + QUEUE_INIT(&env_q); + // Convert env vars to wchar_t and calculate how big the final env block + // needs to be + for (size_t i = 0; i < denv_size; i++) { + EnvNode *env_node = xmalloc(sizeof(*env_node)); + rc = utf8_to_utf16(env[i], -1, &env_node->str); + if (rc != 0) { + goto cleanup; + } + env_node->len = wcslen(env_node->str) + 1; + env_block_len += env_node->len; + QUEUE_INSERT_TAIL(&env_q, &env_node->node); + } + + // Additional '\0' after the final entry + env_block_len++; + + *env_block = xmalloc(sizeof(**env_block) * env_block_len); + wchar_t *pos = *env_block; + + QUEUE_FOREACH(q, &env_q) { + EnvNode *env_node = QUEUE_DATA(q, EnvNode, node); + memcpy(pos, env_node->str, env_node->len * sizeof(*pos)); + pos += env_node->len; + } + + *pos = L'\0'; + +cleanup: + q = QUEUE_HEAD(&env_q); + while (q != &env_q) { + QUEUE *next = q->next; + EnvNode *env_node = QUEUE_DATA(q, EnvNode, node); + XFREE_CLEAR(env_node->str); + QUEUE_REMOVE(q); + xfree(env_node); + q = next; + } + + return rc; +} diff --git a/src/nvim/os/stdpaths.c b/src/nvim/os/stdpaths.c index 66bc990402..93b8d5ca12 100644 --- a/src/nvim/os/stdpaths.c +++ b/src/nvim/os/stdpaths.c @@ -108,6 +108,17 @@ char *get_xdg_home(const XDGVarType idx) return dir; } +/// Return subpath of $XDG_CACHE_HOME +/// +/// @param[in] fname New component of the path. +/// +/// @return [allocated] `$XDG_CACHE_HOME/nvim/{fname}` +char *stdpaths_user_cache_subpath(const char *fname) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET +{ + return concat_fnames_realloc(get_xdg_home(kXDGCacheHome), fname, true); +} + /// Return subpath of $XDG_CONFIG_HOME /// /// @param[in] fname New component of the path. diff --git a/src/nvim/os/time.c b/src/nvim/os/time.c index 4b6533cd0c..5cf628935f 100644 --- a/src/nvim/os/time.c +++ b/src/nvim/os/time.c @@ -97,7 +97,7 @@ void os_microdelay(uint64_t us, bool ignoreinput) const int rv = uv_cond_timedwait(&delay_cond, &delay_mutex, ns_delta); if (0 != rv && UV_ETIMEDOUT != rv) { - assert(false); + abort(); break; } // Else: Timeout proceeded normally. diff --git a/src/nvim/po/af.po b/src/nvim/po/af.po index fa6674469c..db44f50a46 100644 --- a/src/nvim/po/af.po +++ b/src/nvim/po/af.po @@ -3290,10 +3290,6 @@ msgstr "-o[N]\t\tMaak N vensters oop (verstek: een vir elke ler)" #~ msgid " -V[N][file] Verbose [level][file]\n" #~ msgstr "" -#, fuzzy -#~ msgid " -Z Restricted mode\n" -#~ msgstr " vir twee modusse " - #~ msgid " --api-info Write msgpack-encoded API metadata to stdout\n" #~ msgstr "" @@ -7473,9 +7469,6 @@ msgstr "E446: Geen lernaam onder loper" #~ msgid "-b\t\t\tBinary mode" #~ msgstr "-b\t\t\tBinre modus" -#~ msgid "-Z\t\t\tRestricted mode (like \"rvim\")" -#~ msgstr "-Z\t\t\tBeperkte modus (soos \"rvim\")" - #~ msgid "-R\t\t\tReadonly mode (like \"view\")" #~ msgstr "-R\t\t\tLeesalleen modus (soos \"view\")" diff --git a/src/nvim/po/ca.po b/src/nvim/po/ca.po index be4206f36e..6c4d6ddd22 100644 --- a/src/nvim/po/ca.po +++ b/src/nvim/po/ca.po @@ -3469,10 +3469,6 @@ msgstr "-y\t\t\tMode senzill (com \"evim\", sense modes)" msgid "-R\t\t\tReadonly mode (like \"view\")" msgstr "-R\t\t\tMode noms lectura (com \"view\")" -#: ../main.c:2208 -msgid "-Z\t\t\tRestricted mode (like \"rvim\")" -msgstr "-Z\t\t\tMode restringit (com \"rvim)" - #: ../main.c:2209 msgid "-m\t\t\tModifications (writing files) not allowed" msgstr "-m\t\t\tNo permet modificar (escriure) fitxers" diff --git a/src/nvim/po/cs.cp1250.po b/src/nvim/po/cs.cp1250.po index 5b9f3d3a58..859039eb87 100644 --- a/src/nvim/po/cs.cp1250.po +++ b/src/nvim/po/cs.cp1250.po @@ -3547,10 +3547,6 @@ msgstr "-v\t\t\tSnadn reim (stejn jako \"evim\", dn mdy )" msgid "-R\t\t\tReadonly mode (like \"view\")" msgstr "-R\t\t\tReim pouze_pro_ten (jako \"view\")" -#: ../main.c:2208 -msgid "-Z\t\t\tRestricted mode (like \"rvim\")" -msgstr "-Z\t\t\tOmezen reim (stejn jako \"rvim\")" - #: ../main.c:2209 msgid "-m\t\t\tModifications (writing files) not allowed" msgstr "-m\t\t\tZmny (ukldn soubor) zakzny" diff --git a/src/nvim/po/cs.po b/src/nvim/po/cs.po index 31a90dc514..4d9ad58836 100644 --- a/src/nvim/po/cs.po +++ b/src/nvim/po/cs.po @@ -3547,10 +3547,6 @@ msgstr "-v\t\t\tSnadn reim (stejn jako \"evim\", dn mdy )" msgid "-R\t\t\tReadonly mode (like \"view\")" msgstr "-R\t\t\tReim pouze_pro_ten (jako \"view\")" -#: ../main.c:2208 -msgid "-Z\t\t\tRestricted mode (like \"rvim\")" -msgstr "-Z\t\t\tOmezen reim (stejn jako \"rvim\")" - #: ../main.c:2209 msgid "-m\t\t\tModifications (writing files) not allowed" msgstr "-m\t\t\tZmny (ukldn soubor) zakzny" diff --git a/src/nvim/po/da.po b/src/nvim/po/da.po index f35272810b..7a75425019 100644 --- a/src/nvim/po/da.po +++ b/src/nvim/po/da.po @@ -3015,9 +3015,6 @@ msgstr "-y\t\t\tEasy-tilstand (ligesom \"evim\", tilstandsløs)" msgid "-R\t\t\tReadonly mode (like \"view\")" msgstr "-R\t\t\tSkrivebeskyttet tilstand (ligesom \"view\")" -msgid "-Z\t\t\tRestricted mode (like \"rvim\")" -msgstr "-Z\t\t\tRestriktiv tilstand (ligesom \"rvim\")" - msgid "-m\t\t\tModifications (writing files) not allowed" msgstr "-m\t\t\tÆndringer (skrivning af filer) ikke tilladt" diff --git a/src/nvim/po/de.po b/src/nvim/po/de.po index a2e04965e5..740e9e5f6a 100644 --- a/src/nvim/po/de.po +++ b/src/nvim/po/de.po @@ -2895,10 +2895,6 @@ msgstr "-y\t\t\tLeichter Modus (wie \"evim\", ohne Modi)" msgid "-R\t\t\tReadonly mode (like \"view\")" msgstr "-R\t\t\tModus ohne Schreibrechte (wie \"view\")" -#: ../main.c:2186 -msgid "-Z\t\t\tRestricted mode (like \"rvim\")" -msgstr "-Z\t\t\tEingeschrnkter Modus (wie \"rvim\")" - #: ../main.c:2187 msgid "-m\t\t\tModifications (writing files) not allowed" msgstr "-m\t\t\tnderungen (beim Schreiben von Dateien) sind nicht erlaubt" diff --git a/src/nvim/po/en_GB.po b/src/nvim/po/en_GB.po index 7919fc8946..66cdba6f92 100644 --- a/src/nvim/po/en_GB.po +++ b/src/nvim/po/en_GB.po @@ -3368,10 +3368,6 @@ msgstr "" msgid "-R\t\t\tReadonly mode (like \"view\")" msgstr "" -#: ../main.c:2208 -msgid "-Z\t\t\tRestricted mode (like \"rvim\")" -msgstr "" - #: ../main.c:2209 msgid "-m\t\t\tModifications (writing files) not allowed" msgstr "" diff --git a/src/nvim/po/eo.po b/src/nvim/po/eo.po index 99c46c7275..5480e6a4d8 100644 --- a/src/nvim/po/eo.po +++ b/src/nvim/po/eo.po @@ -2989,9 +2989,6 @@ msgstr "-y\t\t\tFacila reĝimo (kiel \"evim\", senreĝima)" msgid "-R\t\t\tReadonly mode (like \"view\")" msgstr "-R\t\t\tNurlegebla reĝimo (kiel \"view\")" -msgid "-Z\t\t\tRestricted mode (like \"rvim\")" -msgstr "-Z\t\t\tLimigita reĝimo (kiel \"rvim\")" - msgid "-m\t\t\tModifications (writing files) not allowed" msgstr "-m\t\t\tŜanĝoj (skribo al dosieroj) nepermeseblaj" diff --git a/src/nvim/po/es.po b/src/nvim/po/es.po index eeea27610d..064484d1a4 100644 --- a/src/nvim/po/es.po +++ b/src/nvim/po/es.po @@ -3524,10 +3524,6 @@ msgstr "-y\t\t\tModo fácil (como \"evim\", sin modo)" msgid "-R\t\t\tReadonly mode (like \"view\")" msgstr "-R\t\t\tModo de solo lectura (como \"view\")" -#: ../main.c:2208 -msgid "-Z\t\t\tRestricted mode (like \"rvim\")" -msgstr "-Z\t\t\tModo restringido (como \"rvim\")" - #: ../main.c:2209 msgid "-m\t\t\tModifications (writing files) not allowed" msgstr "-m\t\t\tModificación de archivos desactivada" diff --git a/src/nvim/po/fi.po b/src/nvim/po/fi.po index 4489139cfb..5986a57488 100644 --- a/src/nvim/po/fi.po +++ b/src/nvim/po/fi.po @@ -3249,10 +3249,6 @@ msgstr "" #~ msgstr " kahta tilaa varten " #, fuzzy -#~ msgid " -Z Restricted mode\n" -#~ msgstr " kahta tilaa varten " - -#, fuzzy #~ msgid " -m Modifications (writing files) not allowed\n" #~ msgstr "-m\t\t\tMuokkaukset (kirjoittaminen tiedostoon) pois käytöstä" @@ -6961,9 +6957,6 @@ msgstr "Lista tai luku tarvitaan" #~ msgid "-b\t\t\tBinary mode" #~ msgstr "-b\t\t\tBinääritila" -#~ msgid "-Z\t\t\tRestricted mode (like \"rvim\")" -#~ msgstr "-Z\t\t\tRajoitettu tila (kuten rvimillä)" - #~ msgid "-R\t\t\tReadonly mode (like \"view\")" #~ msgstr "-R\t\t\tKirjoitussuojattu tila (kuten view'lla)" diff --git a/src/nvim/po/fr.po b/src/nvim/po/fr.po index bb60649c91..5f1ca2fec5 100644 --- a/src/nvim/po/fr.po +++ b/src/nvim/po/fr.po @@ -3231,9 +3231,6 @@ msgstr "-y\t\tMode facile (comme \"evim\", vim sans modes)" msgid "-R\t\t\tReadonly mode (like \"view\")" msgstr "-R\t\tMode lecture seule (comme \"view\")" -msgid "-Z\t\t\tRestricted mode (like \"rvim\")" -msgstr "-Z\t\tMode restreint (comme \"rvim\")" - msgid "-m\t\t\tModifications (writing files) not allowed" msgstr "-m\t\tInterdire l'enregistrement des fichiers" diff --git a/src/nvim/po/ga.po b/src/nvim/po/ga.po index 1104b31c32..bad01d592a 100644 --- a/src/nvim/po/ga.po +++ b/src/nvim/po/ga.po @@ -3022,9 +3022,6 @@ msgstr "-y\t\t\tMd asca (mar \"evim\", gan mhid)" msgid "-R\t\t\tReadonly mode (like \"view\")" msgstr "-R\t\t\tMd inlite amhin (mar \"view\")" -msgid "-Z\t\t\tRestricted mode (like \"rvim\")" -msgstr "-Z\t\t\tMd srianta (mar \"rvim\")" - msgid "-m\t\t\tModifications (writing files) not allowed" msgstr "-m\t\t\tN cheadatear athruithe (.i. scrobh na gcomhad)" diff --git a/src/nvim/po/it.po b/src/nvim/po/it.po index 511f910b71..dfabc4bee0 100644 --- a/src/nvim/po/it.po +++ b/src/nvim/po/it.po @@ -3510,10 +3510,6 @@ msgstr "-y\t\t\tModalit Facile (come \"evim\", senza modalit)" msgid "-R\t\t\tReadonly mode (like \"view\")" msgstr "-R\t\t\tModalit Sola Lettura (come \"view\")" -#: ../main.c:2208 -msgid "-Z\t\t\tRestricted mode (like \"rvim\")" -msgstr "-Z\t\t\tModalit Ristretta (come \"rvim\")" - #: ../main.c:2209 msgid "-m\t\t\tModifications (writing files) not allowed" msgstr "-m\t\t\tRiscritture del file non permesse" diff --git a/src/nvim/po/ja.euc-jp.po b/src/nvim/po/ja.euc-jp.po index 523e9ca4e3..e2cf68f016 100644 --- a/src/nvim/po/ja.euc-jp.po +++ b/src/nvim/po/ja.euc-jp.po @@ -3024,9 +3024,6 @@ msgstr "-y\t\t\t⡼ (\"evim\" Ʊ⡼̵)" msgid "-R\t\t\tReadonly mode (like \"view\")" msgstr "-R\t\t\tɹѥ⡼ (\"view\" Ʊ)" -msgid "-Z\t\t\tRestricted mode (like \"rvim\")" -msgstr "-Z\t\t\t¥⡼ (\"rvim\" Ʊ)" - msgid "-m\t\t\tModifications (writing files) not allowed" msgstr "-m\t\t\tѹ (ե¸) Ǥʤ褦ˤ" diff --git a/src/nvim/po/ja.po b/src/nvim/po/ja.po index 5a69d0c5bf..85a45cd171 100644 --- a/src/nvim/po/ja.po +++ b/src/nvim/po/ja.po @@ -3024,9 +3024,6 @@ msgstr "-y\t\t\tイージーモード (\"evim\" と同じ、モード無)" msgid "-R\t\t\tReadonly mode (like \"view\")" msgstr "-R\t\t\t読込専用モード (\"view\" と同じ)" -msgid "-Z\t\t\tRestricted mode (like \"rvim\")" -msgstr "-Z\t\t\t制限モード (\"rvim\" と同じ)" - msgid "-m\t\t\tModifications (writing files) not allowed" msgstr "-m\t\t\t変更 (ファイル保存時) をできないようにする" diff --git a/src/nvim/po/ko.UTF-8.po b/src/nvim/po/ko.UTF-8.po index 128b238f8b..b99c22caeb 100644 --- a/src/nvim/po/ko.UTF-8.po +++ b/src/nvim/po/ko.UTF-8.po @@ -3438,10 +3438,6 @@ msgstr "-y\t\t\t쉬운 상태 (\"evim\"과 같음, modeless)" msgid "-R\t\t\tReadonly mode (like \"view\")" msgstr "-R\t\t\t읽기 전용 상태 (\"view\"와 같음)" -#: ../main.c:2208 -msgid "-Z\t\t\tRestricted mode (like \"rvim\")" -msgstr "-Z\t\t\t제한된 상태 (\"rvim\"과 같음)" - #: ../main.c:2209 msgid "-m\t\t\tModifications (writing files) not allowed" msgstr "-m\t\t\t수정(파일 쓰기)이 허용되지 않음" diff --git a/src/nvim/po/nb.po b/src/nvim/po/nb.po index 34617ccf18..2285d755cf 100644 --- a/src/nvim/po/nb.po +++ b/src/nvim/po/nb.po @@ -3452,10 +3452,6 @@ msgstr "-y\t\t\tLett modus (som \"evim\", uten modus)" msgid "-R\t\t\tReadonly mode (like \"view\")" msgstr "-R\t\t\tSkrivebeskyttet modus (som \"view\")" -#: ../main.c:2208 -msgid "-Z\t\t\tRestricted mode (like \"rvim\")" -msgstr "-Z\t\t\tBegrenset modus (som \"rvim\")" - #: ../main.c:2209 msgid "-m\t\t\tModifications (writing files) not allowed" msgstr "-m\t\t\tModifisering (lagring av filer) ikke tillatt" diff --git a/src/nvim/po/nl.po b/src/nvim/po/nl.po index 30f34508f5..00d113c83c 100644 --- a/src/nvim/po/nl.po +++ b/src/nvim/po/nl.po @@ -3449,10 +3449,6 @@ msgstr "-y\t\t\tEenvoudige modus (zoals \"evim\", zonder modus)" msgid "-R\t\t\tReadonly mode (like \"view\")" msgstr "-R\t\t\tAlleen-lezen modus (zoals \"view\")" -#: ../main.c:2208 -msgid "-Z\t\t\tRestricted mode (like \"rvim\")" -msgstr "-Z\t\t\tBeperkte modus (zoals \"rvim\")" - #: ../main.c:2209 msgid "-m\t\t\tModifications (writing files) not allowed" msgstr "-m\t\t\tAanpassingen (bestanden opslaan) niet toegestaan" diff --git a/src/nvim/po/no.po b/src/nvim/po/no.po index 34617ccf18..2285d755cf 100644 --- a/src/nvim/po/no.po +++ b/src/nvim/po/no.po @@ -3452,10 +3452,6 @@ msgstr "-y\t\t\tLett modus (som \"evim\", uten modus)" msgid "-R\t\t\tReadonly mode (like \"view\")" msgstr "-R\t\t\tSkrivebeskyttet modus (som \"view\")" -#: ../main.c:2208 -msgid "-Z\t\t\tRestricted mode (like \"rvim\")" -msgstr "-Z\t\t\tBegrenset modus (som \"rvim\")" - #: ../main.c:2209 msgid "-m\t\t\tModifications (writing files) not allowed" msgstr "-m\t\t\tModifisering (lagring av filer) ikke tillatt" diff --git a/src/nvim/po/pl.UTF-8.po b/src/nvim/po/pl.UTF-8.po index f5c452e924..5f1779d1bd 100644 --- a/src/nvim/po/pl.UTF-8.po +++ b/src/nvim/po/pl.UTF-8.po @@ -3417,10 +3417,6 @@ msgstr "-y\t\t\tTryb łatwy (jak \"evim\", bez trybów)" msgid "-R\t\t\tReadonly mode (like \"view\")" msgstr "-R\t\t\tTryb wyłącznie do odczytu (jak \"view\")" -#: ../main.c:2208 -msgid "-Z\t\t\tRestricted mode (like \"rvim\")" -msgstr "-Z\t\t\tTryb ograniczenia (jak \"rvim\")" - #: ../main.c:2209 msgid "-m\t\t\tModifications (writing files) not allowed" msgstr "-m\t\t\tModyfikacje (zapisywanie plików) niedozwolone" diff --git a/src/nvim/po/pt_BR.po b/src/nvim/po/pt_BR.po index 4f39cb5bdb..533d916de1 100644 --- a/src/nvim/po/pt_BR.po +++ b/src/nvim/po/pt_BR.po @@ -6229,10 +6229,6 @@ msgstr "-y\t\t\tModo fácil (como \"evim\", o Vim não modal)" msgid "-R\t\t\tReadonly mode (like \"view\")" msgstr "-R\t\t\tmodo somente-leitura (como \"view\")" -#: ../main.c:2186 -msgid "-Z\t\t\tRestricted mode (like \"rvim\")" -msgstr "-Z\t\t\tmodo restrito (como \"rvim\")" - #: ../main.c:2187 msgid "-m\t\t\tModifications (writing files) not allowed" msgstr "-m\t\t\tNo permitir alteraes (gravao de arquivos)" diff --git a/src/nvim/po/ru.po b/src/nvim/po/ru.po index 62f892d257..3a96ece2fb 100644 --- a/src/nvim/po/ru.po +++ b/src/nvim/po/ru.po @@ -3442,10 +3442,6 @@ msgstr "-y\t\t\tПростой режим (как \"evim\", безрежимны msgid "-R\t\t\tReadonly mode (like \"view\")" msgstr "-R\t\t\tТолько для чтения (как \"view\")" -#: ../main.c:2208 -msgid "-Z\t\t\tRestricted mode (like \"rvim\")" -msgstr "-Z\t\t\tОграниченный режим (как \"rvim\")" - #: ../main.c:2209 msgid "-m\t\t\tModifications (writing files) not allowed" msgstr "-m\t\t\tБез возможности сохранения изменений (записи файлов)" diff --git a/src/nvim/po/sk.cp1250.po b/src/nvim/po/sk.cp1250.po index ced343bf6b..ff95c68a12 100644 --- a/src/nvim/po/sk.cp1250.po +++ b/src/nvim/po/sk.cp1250.po @@ -3450,10 +3450,6 @@ msgstr "-y\t\t\tJednoduch md (rovnak ako \"evim\", bezmdov)" msgid "-R\t\t\tReadonly mode (like \"view\")" msgstr "-R\t\t\tMd iba pre tanie (ako \"view\")" -#: ../main.c:2208 -msgid "-Z\t\t\tRestricted mode (like \"rvim\")" -msgstr "-Z\t\t\tObmedzen md (rovnak ako \"rvim\")" - #: ../main.c:2209 msgid "-m\t\t\tModifications (writing files) not allowed" msgstr "-m\t\t\tZmeny (ukladanie sborov) zakzan" diff --git a/src/nvim/po/sk.po b/src/nvim/po/sk.po index 66b3d5abb1..d35622726f 100644 --- a/src/nvim/po/sk.po +++ b/src/nvim/po/sk.po @@ -3450,10 +3450,6 @@ msgstr "-y\t\t\tJednoduch md (rovnak ako \"evim\", bezmdov)" msgid "-R\t\t\tReadonly mode (like \"view\")" msgstr "-R\t\t\tMd iba pre tanie (ako \"view\")" -#: ../main.c:2208 -msgid "-Z\t\t\tRestricted mode (like \"rvim\")" -msgstr "-Z\t\t\tObmedzen md (rovnak ako \"rvim\")" - #: ../main.c:2209 msgid "-m\t\t\tModifications (writing files) not allowed" msgstr "-m\t\t\tZmeny (ukladanie sborov) zakzan" diff --git a/src/nvim/po/sr.po b/src/nvim/po/sr.po index 4c157658e5..a93a2ec584 100644 --- a/src/nvim/po/sr.po +++ b/src/nvim/po/sr.po @@ -3028,9 +3028,6 @@ msgstr "-y\t\t\tEasy режим (као \"evim\", безрежимни)" msgid "-R\t\t\tReadonly mode (like \"view\")" msgstr "-R\t\t\tReadonly режим (као \"view\")" -msgid "-Z\t\t\tRestricted mode (like \"rvim\")" -msgstr "-Z\t\t\tRestricted режим (као \"rvim\")" - msgid "-m\t\t\tModifications (writing files) not allowed" msgstr "-m\t\t\tИзмене (уписивање датотека) нису дозвољене" diff --git a/src/nvim/po/sv.po b/src/nvim/po/sv.po index db7bada888..d50c9d695d 100644 --- a/src/nvim/po/sv.po +++ b/src/nvim/po/sv.po @@ -5735,10 +5735,6 @@ msgstr "-y\t\t\tLtt lge (som \"evim\", lgesls)" msgid "-R\t\t\tReadonly mode (like \"view\")" msgstr "-R\t\t\tSkrivskyddat lge (som \"view\")" -#: ../main.c:2186 -msgid "-Z\t\t\tRestricted mode (like \"rvim\")" -msgstr "-Z\t\t\tBegrnsat lge (som \"rvim\")" - #: ../main.c:2187 msgid "-m\t\t\tModifications (writing files) not allowed" msgstr "-m\t\t\tModifieringar (skriva filer) inte tilltet" diff --git a/src/nvim/po/uk.po b/src/nvim/po/uk.po index 604e425bd0..f0ae154648 100644 --- a/src/nvim/po/uk.po +++ b/src/nvim/po/uk.po @@ -14,7 +14,7 @@ msgid "" msgstr "" "Project-Id-Version: vim 7.4\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-08-23 18:45+0300\n" +"POT-Creation-Date: 2021-01-18 17:46+0200\n" "PO-Revision-Date: 2020-08-23 20:19+0300\n" "Last-Translator: Анатолій Сахнік <sakhnik@gmail.com>\n" "Language-Team: Ukrainian\n" @@ -23,6 +23,67 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" +msgid "--Deleted--" +msgstr "--Знищено--" + +#, c-format +msgid "auto-removing autocommand: %s <buffer=%d>" +msgstr "Автоматичне знищення автокоманди: %s <буфер=%d>" + +#, c-format +msgid "E367: No such group: \"%s\"" +msgstr "E367: Немає такої групи: «%s»" + +msgid "E936: Cannot delete the current group" +msgstr "E936: Не вдалося знищити цю групу" + +msgid "W19: Deleting augroup that is still in use" +msgstr "W19: Знищення автогрупи, яка все ще використовується" + +#, c-format +msgid "E215: Illegal character after *: %s" +msgstr "E215: Недозволений символ після *: %s" + +#, c-format +msgid "E216: No such event: %s" +msgstr "E216: Немає такої події: %s" + +#, c-format +msgid "E216: No such group or event: %s" +msgstr "E216: Немає такої групи чи події: %s" + +msgid "" +"\n" +"--- Autocommands ---" +msgstr "" +"\n" +"--- Автокоманди ---" + +#, c-format +msgid "E680: <buffer=%d>: invalid buffer number " +msgstr "E680: <буфер=%d>: некоректний номер буфера " + +msgid "E217: Can't execute autocommands for ALL events" +msgstr "E217: Не можу виконувати автокоманди для УСІХ подій" + +msgid "No matching autocommands" +msgstr "Немає відповідних автокоманд" + +msgid "E218: autocommand nesting too deep" +msgstr "E218: Забагато вкладених автокоманд" + +#, c-format +msgid "%s Autocommands for \"%s\"" +msgstr "%s Автокоманди для «%s»" + +#, c-format +msgid "Executing %s" +msgstr "Виконується %s" + +#, c-format +msgid "autocommand %s" +msgstr "автокоманда %s" + msgid "[Location List]" msgstr "[Список місць]" @@ -95,9 +156,15 @@ msgid "" "E89: No write since last change for buffer %<PRId64> (add ! to override)" msgstr "E89: Буфер %<PRId64> має зміни (! щоб не зважати)" +msgid "E948: Job still running (add ! to end the job)" +msgstr "E948: Задача все ще виконується (! щоб закінчити)" + msgid "E37: No write since last change (add ! to override)" msgstr "E37: Зміни не було записано (! щоб не зважати)" +msgid "E948: Job still running" +msgstr "E948: Задача все ще виконується" + msgid "E37: No write since last change" msgstr "E37: Не записано після останніх змін" @@ -465,6 +532,10 @@ msgid "E957: Invalid window number" msgstr "E957: Некоректний номер вікна" #, c-format +msgid "E940: Cannot lock or unlock variable %s" +msgstr "E940: Неможливо заблокувати чи розблокувати змінну %s" + +#, c-format msgid "E734: Wrong variable type for %s=" msgstr "E734: Неправильний тип змінної для %s=" @@ -544,10 +615,6 @@ msgstr "E690: Пропущено «in» після :for" msgid "E108: No such variable: \"%s\"" msgstr "E108: Змінної немає: «%s»" -#, c-format -msgid "E940: Cannot lock or unlock variable %s" -msgstr "E940: Неможливо заблокувати чи розблокувати змінну %s" - msgid "E109: Missing ':' after '?'" msgstr "E109: Бракує ':' після '?'" @@ -950,9 +1017,6 @@ msgstr "E684: Індекс списку поза межами: %<PRId64>" msgid "E686: Argument of %s must be a List" msgstr "E686: Аргумент у %s має бути списком" -msgid "E928: String required" -msgstr "E928: Потрібен String" - #, c-format msgid "Error converting the call result: %s" msgstr "Не вдалося перетворити результат виклику: %s" @@ -1591,6 +1655,10 @@ msgid "Close \"%s\"?" msgstr "Закрити «%s»?" #, c-format +msgid "E947: Job still running in buffer \"%s\"" +msgstr "E947: Задача все ще запущена у буфері «%s»" + +#, c-format msgid "E162: No write since last change for buffer \"%s\"" msgstr "E162: Буфер «%s» має незбережені зміни" @@ -1615,18 +1683,6 @@ msgid "E666: compiler not supported: %s" msgstr "E666: Компілятор не підтримується: %s" #, c-format -msgid "Searching for \"%s\" in \"%s\"" -msgstr "Пошук «%s» в «%s»" - -#, c-format -msgid "Searching for \"%s\"" -msgstr "Пошук «%s»" - -#, c-format -msgid "not found in '%s': \"%s\"" -msgstr "не знайдено в '%s': «%s»" - -#, c-format msgid ":source error parsing command %s" msgstr ":source помилка розбору команди %s" @@ -1702,12 +1758,16 @@ msgstr "Режим Ex. Для повернення до нормального msgid "E501: At end-of-file" msgstr "E501: Кінець файлу" -msgid "E169: Command too recursive" -msgstr "E169: Команда занадто рекурсивна" +#, c-format +msgid "Executing: %s" +msgstr "Виконується: %s" msgid "line %" msgstr "рядок %" +msgid "E169: Command too recursive" +msgstr "E169: Команда занадто рекурсивна" + #, c-format msgid "E605: Exception not caught: %s" msgstr "E605: Виняткова ситуація не оброблена: %s" @@ -1724,9 +1784,6 @@ msgstr "E464: Неоднозначний вжиток команди корис msgid "E492: Not an editor command" msgstr "E492: Це не команда редактора" -msgid "E981: Command not allowed in restricted mode" -msgstr "E981: Команду не дозволено у обмеженому режимі" - msgid "E493: Backwards range given" msgstr "E493: Інтервал задано навиворіт" @@ -1736,6 +1793,11 @@ msgstr "Інтервал задано навиворіт, щоб помінят msgid "E494: Use w or w>>" msgstr "E494: Спробуйте w або w>>" +msgid "" +"INTERNAL: Cannot use EX_DFLALL with ADDR_NONE, ADDR_UNSIGNED or ADDR_QUICKFIX" +msgstr "" +"ВНУТРІШНЄ: Не можна вживати EX_DFLALL з ADDR_NONE, ADDR_UNSIGNED чи ADDR_QUICKFIX" + msgid "E943: Command table needs to be updated, run 'make'" msgstr "E943: Потрібно поновити таблицю команд, запустіть 'make'" @@ -1762,10 +1824,10 @@ msgstr "E174: Команда вже існує, ! щоб замінити її: msgid "" "\n" -" Name Args Address Complete Definition" +" Name Args Address Complete Definition" msgstr "" "\n" -" Назва Арг. Адреса Доповнення Визначення" +" Назва Арг. Адреса Доповнення Визначення" msgid "No user-defined commands found" msgstr "Не знайдено команд користувача" @@ -2077,12 +2139,12 @@ msgstr "E347: У шляху пошуку більше немає файлів « msgid "E812: Autocommands changed buffer or buffer name" msgstr "E812: Автокоманди змінили буфер чи його назву" -msgid "Illegal file name" -msgstr "Недозволена назва файлу" - msgid "is a directory" msgstr "каталог" +msgid "Illegal file name" +msgstr "Недозволена назва файлу" + msgid "is not a file" msgstr "не файл" @@ -2104,9 +2166,6 @@ msgstr "E201: Автокоманди *ReadPre не повинні змінюва msgid "E202: Conversion made file unreadable!" msgstr "E202: Конвертація унеможливила читання файлу!" -msgid "[fifo/socket]" -msgstr "[канал/сокет]" - msgid "[fifo]" msgstr "[канал]" @@ -2353,67 +2412,6 @@ msgstr "E462: Не вдалося підготувати «%s», щоб пере msgid "E321: Could not reload \"%s\"" msgstr "E321: Не вдалося перечитати «%s»" -msgid "--Deleted--" -msgstr "--Знищено--" - -#, c-format -msgid "auto-removing autocommand: %s <buffer=%d>" -msgstr "Автоматичне знищення автокоманди: %s <буфер=%d>" - -#, c-format -msgid "E367: No such group: \"%s\"" -msgstr "E367: Немає такої групи: «%s»" - -msgid "E936: Cannot delete the current group" -msgstr "E936: Не вдалося знищити цю групу" - -msgid "W19: Deleting augroup that is still in use" -msgstr "W19: Знищення автогрупи, яка все ще використовується" - -#, c-format -msgid "E215: Illegal character after *: %s" -msgstr "E215: Недозволений символ після *: %s" - -#, c-format -msgid "E216: No such event: %s" -msgstr "E216: Немає такої події: %s" - -#, c-format -msgid "E216: No such group or event: %s" -msgstr "E216: Немає такої групи чи події: %s" - -msgid "" -"\n" -"--- Autocommands ---" -msgstr "" -"\n" -"--- Автокоманди ---" - -#, c-format -msgid "E680: <buffer=%d>: invalid buffer number " -msgstr "E680: <буфер=%d>: некоректний номер буфера " - -msgid "E217: Can't execute autocommands for ALL events" -msgstr "E217: Не можу виконувати автокоманди для УСІХ подій" - -msgid "No matching autocommands" -msgstr "Немає відповідних автокоманд" - -msgid "E218: autocommand nesting too deep" -msgstr "E218: Забагато вкладених автокоманд" - -#, c-format -msgid "%s Autocommands for \"%s\"" -msgstr "%s Автокоманди для «%s»" - -#, c-format -msgid "Executing %s" -msgstr "Виконується %s" - -#, c-format -msgid "autocommand %s" -msgstr "автокоманда %s" - msgid "E219: Missing {." msgstr "E219: Бракує {." @@ -2519,9 +2517,6 @@ msgstr "E685: Внутрішня помилка: %s" msgid "Interrupted" msgstr "Перервано" -msgid "E14: Invalid address" -msgstr "E14: Неправильна адреса" - msgid "E474: Invalid argument" msgstr "E474: Некоректний аргумент" @@ -2719,6 +2714,9 @@ msgstr "E45: Встановлено опцію 'readonly' (! щоб не зва msgid "E46: Cannot change read-only variable \"%.*s\"" msgstr "E46: Змінна тільки для читання: «%.*s»" +msgid "E928: String required" +msgstr "E928: Потрібен String" + msgid "E715: Dictionary required" msgstr "E715: Потрібен словник" @@ -2727,8 +2725,8 @@ msgid "E118: Too many arguments for function: %s" msgstr "E118: Забагато аргументів для функції: %s" #, c-format -msgid "E716: Key not present in Dictionary: %s" -msgstr "E716: Немає такого ключа у словнику: %s" +msgid "E716: Key not present in Dictionary: \"%s\"" +msgstr "E716: Немає такого ключа у словнику: «%s»" msgid "E714: List required" msgstr "E714: Потрібен список" @@ -3158,6 +3156,10 @@ msgid "E5106: Error while creating shared module: %.*s" msgstr "E5106: Помилка створення розділюваного модуля: %.*s" #, c-format +msgid "E5106: Error while creating inspect module: %.*s" +msgstr "E5106: Помилка створення модуля inspect: %.*s" + +#, c-format msgid "E5106: Error while creating vim module: %.*s" msgstr "E5106: Помилка створення модуля vim: %.*s" @@ -3165,10 +3167,6 @@ msgid "E970: Failed to initialize lua interpreter" msgstr "E970: Не вдалося ініціалізувати інтерпретатор lua" #, c-format -msgid "E5117: Error while updating package paths: %.*s" -msgstr "E5117: Помилка оновлення шляхів пакунку: %.*s" - -#, c-format msgid "E5114: Error while converting print argument #%i: %.*s" msgstr "E5114: Не вдалося перетворити аргумент #%i друку: %.*s" @@ -3256,6 +3254,10 @@ msgid "pre-vimrc command line" msgstr "команди перед vimrc" #, c-format +msgid "Conflicting configs: \"%s\" \"%s\"" +msgstr "Суперечливі конфігурації: «%s» «%s»" + +#, c-format msgid "E282: Cannot read from \"%s\"" msgstr "E282: Не вдалося прочитати з «%s»" @@ -3367,9 +3369,6 @@ msgstr " -v, --version Надрукувати інформацію пр msgid " -V[N][file] Verbose [level][file]\n" msgstr " -V[N][файл] Більше повідомлень [рівень][файл]\n" -msgid " -Z Restricted mode\n" -msgstr " -Z Обмежений режим\n" - msgid " --api-info Write msgpack-encoded API metadata to stdout\n" msgstr "" " --api-info Записати метадані API, серіалізовані у msgpack, у " @@ -4005,8 +4004,7 @@ msgstr "" "Введіть :qa! і натисність <Enter> щоб відкинути всі зміни і вийти Nvim" msgid "Type :qa and press <Enter> to exit Nvim" -msgstr "" -"Введіть :qa і натисність <Enter> щоб вийти з Nvim" +msgstr "Введіть :qa і натисність <Enter> щоб вийти з Nvim" #, c-format msgid "1 line %sed 1 time" @@ -4071,10 +4069,10 @@ msgstr "E353: У регістрі %s нічого немає" msgid "" "\n" -"--- Registers ---" +"Type Name Content" msgstr "" "\n" -"--- Регістри ---" +"Тип Наз. Вміст " msgid "" "E883: search pattern and expression register may not contain two or more " @@ -4187,9 +4185,6 @@ msgstr "E537: 'commentstring' має бути порожньою чи місти msgid "E540: Unclosed expression sequence" msgstr "E540: Послідовність виразів не завершено" -msgid "E541: too many items" -msgstr "E541: Забагато елементів" - msgid "E542: unbalanced groups" msgstr "E542: Групи не збалансовано" @@ -4277,6 +4272,9 @@ msgstr "" msgid "E5677: Error writing input to shell-command: %s" msgstr "E5677: Не вдалося записати на вхід команди оболонки: %s" +msgid "%a %b %d %H:%M:%S %Y" +msgstr "%H:%M:%S %a, %d %B %Y р." + #, c-format msgid "E447: Can't find file \"%s\" in path" msgstr "E447: Файл «%s» не знайдено у шляху пошуку" @@ -4284,8 +4282,11 @@ msgstr "E447: Файл «%s» не знайдено у шляху пошуку" msgid "E553: No more items" msgstr "E553: Немає більше елементів" +msgid "E925: Current quickfix list was changed" +msgstr "E925: Поточний список quickfix змінився" + msgid "E926: Current location list was changed" -msgstr "E926: Цей список місць було змінено" +msgstr "E926: Поточний список місць змінився" #, c-format msgid "E372: Too many %%%c in format string" @@ -4319,9 +4320,6 @@ msgstr "E379: Пропущена чи порожня назва каталогу msgid "E924: Current window was closed" msgstr "E924: Активне вікно було закрито" -msgid "E925: Current quickfix was changed" -msgstr "E925: Цей quickfix було змінено" - #, c-format msgid "(%d of %d)%s%s: " msgstr "(%d з %d)%s%s: " @@ -4349,6 +4347,9 @@ msgstr "E683: Пропущено назву файлу чи некоректни msgid "Cannot open file \"%s\"" msgstr "Не вдалося відкрити файл «%s»" +msgid "cannot have both a list and a \"what\" argument" +msgstr "не можна задавати одночасно список і аргумент «що»" + msgid "E681: Buffer is not loaded" msgstr "E681: Буфер не завантажено" @@ -4469,6 +4470,18 @@ msgstr "" msgid "Switching to backtracking RE engine for pattern: " msgstr "Перемикання до простого рушія регулярних виразів: " +#, c-format +msgid "Searching for \"%s\" in \"%s\"" +msgstr "Пошук «%s» в «%s»" + +#, c-format +msgid "Searching for \"%s\"" +msgstr "Пошук «%s»" + +#, c-format +msgid "not found in '%s': \"%s\"" +msgstr "не знайдено в '%s': «%s»" + msgid " TERMINAL" msgstr " ТЕРМІНАЛ" @@ -5252,6 +5265,9 @@ msgstr "синхронізується по коментарях стилю С" msgid "no syncing" msgstr "без синхронізації" +msgid "syncing starts at the first line" +msgstr "синхронізація починається з першого рядка" + msgid "syncing starts " msgstr "починається синхронізація за " @@ -5283,6 +5299,9 @@ msgstr "" msgid "E392: No such syntax cluster: %s" msgstr "E392: Немає такого синтаксичного кластера: %s" +msgid "from the first line" +msgstr "з першого рядка" + msgid "minimal " msgstr "мінімальний " diff --git a/src/nvim/po/vi.po b/src/nvim/po/vi.po index a954ea6e34..c693f910d8 100644 --- a/src/nvim/po/vi.po +++ b/src/nvim/po/vi.po @@ -3479,10 +3479,6 @@ msgstr "-y\t\t\tChế độ đơn giản (giống \"evim\", không có chế đ msgid "-R\t\t\tReadonly mode (like \"view\")" msgstr "-R\t\t\tChế độ chỉ đọc (giống \"view\")" -#: ../main.c:2208 -msgid "-Z\t\t\tRestricted mode (like \"rvim\")" -msgstr "-Z\t\t\tChế độ hạn chế (giống \"rvim\")" - #: ../main.c:2209 msgid "-m\t\t\tModifications (writing files) not allowed" msgstr "-m\t\t\tKhông có khả năng ghi nhớ thay đổi (ghi nhớ tập tin)" diff --git a/src/nvim/po/zh_CN.UTF-8.po b/src/nvim/po/zh_CN.UTF-8.po index 542157002a..1e329443ce 100644 --- a/src/nvim/po/zh_CN.UTF-8.po +++ b/src/nvim/po/zh_CN.UTF-8.po @@ -3427,10 +3427,6 @@ msgstr "-y\t\t\t容易模式 (同 \"evim\",无模式)" msgid "-R\t\t\tReadonly mode (like \"view\")" msgstr "-R\t\t\t只读模式 (同 \"view\")" -#: ../main.c:2208 -msgid "-Z\t\t\tRestricted mode (like \"rvim\")" -msgstr "-Z\t\t\t限制模式 (同 \"rvim\")" - #: ../main.c:2209 msgid "-m\t\t\tModifications (writing files) not allowed" msgstr "-m\t\t\t不可修改(写入文件)" diff --git a/src/nvim/po/zh_TW.UTF-8.po b/src/nvim/po/zh_TW.UTF-8.po index 6a11b5e669..c97f31ddcf 100644 --- a/src/nvim/po/zh_TW.UTF-8.po +++ b/src/nvim/po/zh_TW.UTF-8.po @@ -3482,10 +3482,6 @@ msgstr "-y\t\t\t簡易模式 (同 \"evim\", modeless)" msgid "-R\t\t\tReadonly mode (like \"view\")" msgstr "-R\t\t\t唯讀模式 (同 \"view\")" -#: ../main.c:2208 -msgid "-Z\t\t\tRestricted mode (like \"rvim\")" -msgstr "-Z\t\t\t限制模式 (同 \"rvim\")" - #: ../main.c:2209 msgid "-m\t\t\tModifications (writing files) not allowed" msgstr "-m\t\t\t不可修改 (寫入檔案)" diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index a625c09f78..e074f73d04 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -5264,7 +5264,7 @@ void ex_vimgrep(exarg_T *eap) qf_new_list(qi, title); } - // parse the list of arguments + // Parse the list of arguments, wildcards have already been expanded. if (get_arglist_exp(p, &fcount, &fnames, true) == FAIL) { goto theend; } @@ -5648,7 +5648,7 @@ static int get_qfline_items(qfline_T *qfp, list_T *list) == FAIL)) { // tv_dict_add* fail only if key already exist, but this is a newly // allocated dictionary which is thus guaranteed to have no existing keys. - assert(false); + abort(); } return OK; diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 377e8f58fa..a78f905a70 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -2417,8 +2417,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } } - if (wp->w_p_list) { - if (curwin->w_p_lcs_chars.space + if (wp->w_p_list && !has_fold) { + if (wp->w_p_lcs_chars.space || wp->w_p_lcs_chars.trail || wp->w_p_lcs_chars.nbsp) { extra_check = true; @@ -2883,8 +2883,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } if (draw_state == WL_LINE - && foldinfo.fi_level != 0 - && foldinfo.fi_lines > 0 + && has_fold && vcol == 0 && n_extra == 0 && row == startrow) { @@ -2905,8 +2904,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } if (draw_state == WL_LINE - && foldinfo.fi_level != 0 - && foldinfo.fi_lines > 0 + && has_fold && col < grid->Columns && n_extra == 0 && row == startrow) { @@ -2918,8 +2916,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } if (draw_state == WL_LINE - && foldinfo.fi_level != 0 - && foldinfo.fi_lines > 0 + && has_fold && col >= grid->Columns && n_extra != 0 && row == startrow) { @@ -3087,7 +3084,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, || vcol < fromcol || vcol_prev < fromcol_prev || vcol >= tocol)) { char_attr = line_attr; - } else { + } else { attr_pri = false; if (has_syntax) { char_attr = syntax_attr; @@ -3742,7 +3739,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } wp->w_wrow = row; did_wcol = true; - curwin->w_valid |= VALID_WCOL|VALID_WROW|VALID_VIRTCOL; + wp->w_valid |= VALID_WCOL|VALID_WROW|VALID_VIRTCOL; } // Don't override visual selection highlighting. @@ -3835,9 +3832,9 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, // Add a blank character to highlight. schar_from_ascii(linebuf_char[off], ' '); } - if (area_attr == 0) { - /* Use attributes from match with highest priority among - * 'search_hl' and the match list. */ + if (area_attr == 0 && !has_fold) { + // Use attributes from match with highest priority among + // 'search_hl' and the match list. char_attr = search_hl.attr; cur = wp->w_match_head; shl_flag = FALSE; @@ -4053,6 +4050,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, && !wp->w_p_wrap && filler_todo <= 0 && (wp->w_p_rl ? col == 0 : col == grid->Columns - 1) + && !has_fold && (*ptr != NUL || lcs_eol_one > 0 || (n_extra && (c_extra != NUL || *p_extra != NUL)))) { diff --git a/src/nvim/search.c b/src/nvim/search.c index 787a464070..2802da6f7f 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -4497,9 +4497,9 @@ find_pattern_in_path( regmatch_T regmatch; regmatch_T incl_regmatch; regmatch_T def_regmatch; - int matched = FALSE; - int did_show = FALSE; - int found = FALSE; + bool matched = false; + bool did_show = false; + bool found = false; int i; char_u *already = NULL; char_u *startp = NULL; @@ -4611,7 +4611,7 @@ find_pattern_in_path( } MSG_PUTS_TITLE(_("in path ---\n")); } - did_show = TRUE; + did_show = true; while (depth_displayed < depth && !got_int) { ++depth_displayed; for (i = 0; i < depth_displayed; i++) @@ -4761,10 +4761,10 @@ search_line: matched = !STRNCMP(startp, ptr, len); if (matched && define_matched && whole && vim_iswordc(startp[len])) - matched = FALSE; + matched = false; } else if (regmatch.regprog != NULL && vim_regexec(®match, line, (colnr_T)(p - line))) { - matched = TRUE; + matched = true; startp = regmatch.startp[0]; // Check if the line is not a comment line (unless we are // looking for a define). A line starting with "# define" @@ -4789,15 +4789,16 @@ search_line: if (matched && p[0] == '/' && (p[1] == '*' || p[1] == '/')) { - matched = FALSE; - /* After "//" all text is comment */ - if (p[1] == '/') + matched = false; + // After "//" all text is comment + if (p[1] == '/') { break; - ++p; + } + p++; } else if (!matched && p[0] == '*' && p[1] == '/') { - /* Can find match after "* /". */ - matched = TRUE; - ++p; + // Can find match after "* /". + matched = true; + p++; } } } @@ -4811,7 +4812,7 @@ search_line: if (depth == -1 && lnum == curwin->w_cursor.lnum) break; - found = TRUE; + found = true; aux = p = startp; if (compl_cont_status & CONT_ADDING) { p += compl_length; @@ -4879,9 +4880,10 @@ search_line: break; } } else if (action == ACTION_SHOW_ALL) { - found = TRUE; - if (!did_show) - gotocmdline(TRUE); /* cursor at status line */ + found = true; + if (!did_show) { + gotocmdline(true); // cursor at status line + } if (curr_fname != prev_fname) { if (did_show) msg_putchar('\n'); /* cursor below last one */ @@ -4890,28 +4892,28 @@ search_line: msg_home_replace_hl(curr_fname); prev_fname = curr_fname; } - did_show = TRUE; - if (!got_int) - show_pat_in_path(line, type, TRUE, action, - (depth == -1) ? NULL : files[depth].fp, - (depth == -1) ? &lnum : &files[depth].lnum, - match_count++); + did_show = true; + if (!got_int) { + show_pat_in_path(line, type, true, action, + (depth == -1) ? NULL : files[depth].fp, + (depth == -1) ? &lnum : &files[depth].lnum, + match_count++); + } /* Set matched flag for this file and all the ones that * include it */ for (i = 0; i <= depth; ++i) files[i].matched = TRUE; } else if (--count <= 0) { - found = TRUE; + found = true; if (depth == -1 && lnum == curwin->w_cursor.lnum - && l_g_do_tagpreview == 0 - ) + && l_g_do_tagpreview == 0) { EMSG(_("E387: Match is on current line")); - else if (action == ACTION_SHOW) { + } else if (action == ACTION_SHOW) { show_pat_in_path(line, type, did_show, action, - (depth == -1) ? NULL : files[depth].fp, - (depth == -1) ? &lnum : &files[depth].lnum, 1L); - did_show = TRUE; + (depth == -1) ? NULL : files[depth].fp, + (depth == -1) ? &lnum : &files[depth].lnum, 1L); + did_show = true; } else { /* ":psearch" uses the preview window */ if (l_g_do_tagpreview != 0) { @@ -4960,15 +4962,16 @@ search_line: break; } exit_matched: - matched = FALSE; - /* look for other matches in the rest of the line if we - * are not at the end of it already */ + matched = false; + // look for other matches in the rest of the line if we + // are not at the end of it already if (def_regmatch.regprog == NULL && action == ACTION_EXPAND && !(compl_cont_status & CONT_SOL) && *startp != NUL - && *(p = startp + utfc_ptr2len(startp)) != NUL) + && *(p = startp + utfc_ptr2len(startp)) != NUL) { goto search_line; + } } line_breakcheck(); if (action == ACTION_EXPAND) @@ -5046,16 +5049,20 @@ fpip_end: vim_regfree(def_regmatch.regprog); } -static void show_pat_in_path(char_u *line, int type, int did_show, int action, FILE *fp, linenr_T *lnum, long count) +static void show_pat_in_path(char_u *line, int type, bool did_show, int action, + FILE *fp, linenr_T *lnum, long count) + FUNC_ATTR_NONNULL_ARG(1, 6) { char_u *p; - if (did_show) - msg_putchar('\n'); /* cursor below last one */ - else if (!msg_silent) - gotocmdline(TRUE); /* cursor at status line */ - if (got_int) /* 'q' typed at "--more--" message */ + if (did_show) { + msg_putchar('\n'); // cursor below last one + } else if (!msg_silent) { + gotocmdline(true); // cursor at status line + } + if (got_int) { // 'q' typed at "--more--" message return; + } for (;; ) { p = line + STRLEN(line) - 1; if (fp != NULL) { diff --git a/src/nvim/shada.c b/src/nvim/shada.c index 2444910bb3..c0e787380f 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -765,7 +765,7 @@ static ShaDaReadResult sd_reader_skip(ShaDaReadDef *const sd_reader, (uint64_t) offset); return kSDReadStatusNotShaDa; } - assert(false); + abort(); } return kSDReadStatusSuccess; } @@ -1224,7 +1224,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) } case kSDReadStatusFinished: { // Should be handled by the while condition. - assert(false); + abort(); } case kSDReadStatusNotShaDa: case kSDReadStatusReadError: { @@ -1236,7 +1236,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) } switch (cur_entry.type) { case kSDItemMissing: { - assert(false); + abort(); } case kSDItemUnknown: { break; @@ -1628,7 +1628,7 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, ((size_t) (!CHECK_DEFAULT(entry, attr))) switch (entry.type) { case kSDItemMissing: { - assert(false); + abort(); } case kSDItemUnknown: { if (spacker->callback(spacker->data, entry.data.unknown_item.contents, @@ -1850,7 +1850,7 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, break; } default: { - assert(false); + abort(); } } } @@ -2147,7 +2147,7 @@ static inline ShaDaWriteResult shada_read_when_writing( } case kSDReadStatusFinished: { // Should be handled by the while condition. - assert(false); + abort(); } case kSDReadStatusNotShaDa: { ret = kSDWriteReadNotShada; @@ -2184,7 +2184,7 @@ static inline ShaDaWriteResult shada_read_when_writing( } case kSDItemHeader: case kSDItemBufferList: { - assert(false); + abort(); } case kSDItemUnknown: { ret = shada_pack_entry(packer, entry, 0); @@ -4044,7 +4044,7 @@ shada_read_next_item_start: } case kSDItemMissing: case kSDItemUnknown: { - assert(false); + abort(); } } entry->type = (ShadaEntryType) type_u64; diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 5714f5e425..6425c9fed5 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -3123,6 +3123,7 @@ spell_find_suggest ( static bool expr_busy = false; int c; langp_T *lp; + bool did_intern = false; // Set the info in "*su". memset(su, 0, sizeof(suginfo_T)); @@ -3206,14 +3207,16 @@ spell_find_suggest ( spell_suggest_expr(su, buf + 5); expr_busy = false; } - } else if (STRNCMP(buf, "file:", 5) == 0) + } else if (STRNCMP(buf, "file:", 5) == 0) { // Use list of suggestions in a file. spell_suggest_file(su, buf + 5); - else { - // Use internal method. + } else if (!did_intern) { + // Use internal method once. spell_suggest_intern(su, interactive); - if (sps_flags & SPS_DOUBLE) + if (sps_flags & SPS_DOUBLE) { do_combine = true; + } + did_intern = true; } } diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c index 90af010164..3c125959a9 100644 --- a/src/nvim/spellfile.c +++ b/src/nvim/spellfile.c @@ -5387,7 +5387,8 @@ spell_add_word ( len, word, NameBuff); } } - if (fseek(fd, fpos_next, SEEK_SET) <= 0) { + if (fseek(fd, fpos_next, SEEK_SET) != 0) { + PERROR(_("Seek error in spellfile")); break; } } diff --git a/src/nvim/tag.c b/src/nvim/tag.c index c6b1a0d04c..84ca240734 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -1461,7 +1461,7 @@ find_tags( p_ic = ignorecase_opt(pat, true, true); break; default: - assert(false); + abort(); } help_save = curbuf->b_help; diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 39e2ca6171..642c443318 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -535,8 +535,44 @@ void terminal_send(Terminal *term, char *data, size_t size) term->opts.write_cb(data, size, term->opts.data); } +static bool is_filter_char(int c) +{ + unsigned int flag = 0; + switch (c) { + case 0x08: + flag = TPF_BS; + break; + case 0x09: + flag = TPF_HT; + break; + case 0x0A: + case 0x0D: + break; + case 0x0C: + flag = TPF_FF; + break; + case 0x1b: + flag = TPF_ESC; + break; + case 0x7F: + flag = TPF_DEL; + break; + default: + if (c < ' ') { + flag = TPF_C0; + } else if (c >= 0x80 && c <= 0x9F) { + flag = TPF_C1; + } + } + return !!(tpf_flags & flag); +} + void terminal_paste(long count, char_u **y_array, size_t y_size) { + vterm_keyboard_start_paste(curbuf->terminal->vt); + terminal_flush_output(curbuf->terminal); + size_t buff_len = STRLEN(y_array[0]); + char_u *buff = xmalloc(buff_len); for (int i = 0; i < count; i++) { // -V756 // feed the lines to the terminal for (size_t j = 0; j < y_size; j++) { @@ -544,9 +580,28 @@ void terminal_paste(long count, char_u **y_array, size_t y_size) // terminate the previous line terminal_send(curbuf->terminal, "\n", 1); } - terminal_send(curbuf->terminal, (char *)y_array[j], STRLEN(y_array[j])); + size_t len = STRLEN(y_array[j]); + if (len > buff_len) { + buff = xrealloc(buff, len); + buff_len = len; + } + char_u *dst = buff; + char_u *src = y_array[j]; + while (*src != '\0') { + len = (size_t)utf_ptr2len(src); + int c = utf_ptr2char(src); + if (!is_filter_char(c)) { + memcpy(dst, src, len); + dst += len; + } + src += len; + } + terminal_send(curbuf->terminal, (char *)buff, (size_t)(dst - buff)); } } + xfree(buff); + vterm_keyboard_end_paste(curbuf->terminal->vt); + terminal_flush_output(curbuf->terminal); } void terminal_flush_output(Terminal *term) diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index 641e98ab30..1f3a45a9ab 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -1110,14 +1110,14 @@ func Test_BufReadCmd() endfunc func SetChangeMarks(start, end) - exe a:start. 'mark [' - exe a:end. 'mark ]' + exe a:start .. 'mark [' + exe a:end .. 'mark ]' endfunc " Verify the effects of autocmds on '[ and '] func Test_change_mark_in_autocmds() edit! Xtest - call feedkeys("ia\<CR>b\<CR>c\<CR>d\<C-g>u", 'xtn') + call feedkeys("ia\<CR>b\<CR>c\<CR>d\<C-g>u\<Esc>", 'xtn') call SetChangeMarks(2, 3) write @@ -1279,26 +1279,9 @@ func Test_TextYankPost() bwipe! endfunc -func Test_nocatch_wipe_all_buffers() - " Real nasty autocommand: wipe all buffers on any event. - au * * bwipe * - call assert_fails('next x', 'E93') - bwipe - au! -endfunc - -func Test_nocatch_wipe_dummy_buffer() - " Nasty autocommand: wipe buffer on any event. - au * x bwipe - call assert_fails('lv½ /x', 'E480') - au! -endfunc - -func Test_wipe_cbuffer() - sv x - au * * bw - lb - au! +func Test_autocommand_all_events() + call assert_fails('au * * bwipe', 'E1155:') + call assert_fails('au * x bwipe', 'E1155:') endfunc " Test TextChangedI and TextChangedP @@ -1956,4 +1939,15 @@ func Test_autocmd_window() %bw! endfunc +func Test_autocmd_closes_window() + au BufNew,BufWinLeave * e %e + file yyy + au BufNew,BufWinLeave * ball + call assert_fails('n xxx', 'E143:') + + bwipe % + au! BufNew + au! BufWinLeave +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_backspace_opt.vim b/src/nvim/testdir/test_backspace_opt.vim new file mode 100644 index 0000000000..d680b442db --- /dev/null +++ b/src/nvim/testdir/test_backspace_opt.vim @@ -0,0 +1,151 @@ +" Tests for 'backspace' settings + +func Exec(expr) + let str='' + try + exec a:expr + catch /.*/ + let str=v:exception + endtry + return str +endfunc + +func Test_backspace_option() + set backspace= + call assert_equal('', &backspace) + set backspace=indent + call assert_equal('indent', &backspace) + set backspace=eol + call assert_equal('eol', &backspace) + set backspace=start + call assert_equal('start', &backspace) + set backspace=nostop + call assert_equal('nostop', &backspace) + " Add the value + set backspace= + set backspace=indent + call assert_equal('indent', &backspace) + set backspace+=eol + call assert_equal('indent,eol', &backspace) + set backspace+=start + call assert_equal('indent,eol,start', &backspace) + set backspace+=nostop + call assert_equal('indent,eol,start,nostop', &backspace) + " Delete the value + set backspace-=nostop + call assert_equal('indent,eol,start', &backspace) + set backspace-=indent + call assert_equal('eol,start', &backspace) + set backspace-=start + call assert_equal('eol', &backspace) + set backspace-=eol + call assert_equal('', &backspace) + " Check the error + call assert_equal(0, match(Exec('set backspace=ABC'), '.*E474')) + call assert_equal(0, match(Exec('set backspace+=def'), '.*E474')) + " NOTE: Vim doesn't check following error... + "call assert_equal(0, match(Exec('set backspace-=ghi'), '.*E474')) + + " Check backwards compatibility with version 5.4 and earlier + set backspace=0 + call assert_equal('0', &backspace) + set backspace=1 + call assert_equal('1', &backspace) + set backspace=2 + call assert_equal('2', &backspace) + set backspace=3 + call assert_equal('3', &backspace) + call assert_false(match(Exec('set backspace=4'), '.*E474')) + call assert_false(match(Exec('set backspace=10'), '.*E474')) + + " Cleared when 'compatible' is set + " set compatible + " call assert_equal('', &backspace) + set nocompatible viminfo+=nviminfo +endfunc + +" Test with backspace set to the non-compatible setting +func Test_backspace_ctrl_u() + new + call append(0, [ + \ "1 this shouldn't be deleted", + \ "2 this shouldn't be deleted", + \ "3 this shouldn't be deleted", + \ "4 this should be deleted", + \ "5 this shouldn't be deleted", + \ "6 this shouldn't be deleted", + \ "7 this shouldn't be deleted", + \ "8 this shouldn't be deleted (not touched yet)"]) + call cursor(2, 1) + + " set compatible + set backspace=2 + + exe "normal Avim1\<C-U>\<Esc>\<CR>" + exe "normal Avim2\<C-G>u\<C-U>\<Esc>\<CR>" + + set cpo-=< + inoremap <c-u> <left><c-u> + exe "normal Avim3\<C-U>\<Esc>\<CR>" + iunmap <c-u> + exe "normal Avim4\<C-U>\<C-U>\<Esc>\<CR>" + + " Test with backspace set to the compatible setting + set backspace= visualbell + exe "normal A vim5\<Esc>A\<C-U>\<C-U>\<Esc>\<CR>" + exe "normal A vim6\<Esc>Azwei\<C-G>u\<C-U>\<Esc>\<CR>" + + inoremap <c-u> <left><c-u> + exe "normal A vim7\<C-U>\<C-U>\<Esc>\<CR>" + + call assert_equal([ + \ "1 this shouldn't be deleted", + \ "2 this shouldn't be deleted", + \ "3 this shouldn't be deleted", + \ "4 this should be deleted3", + \ "", + \ "6 this shouldn't be deleted vim5", + \ "7 this shouldn't be deleted vim6", + \ "8 this shouldn't be deleted (not touched yet) vim7", + \ ""], getline(1, '$')) + + " Reset values + set compatible&vim + set visualbell&vim + set backspace&vim + + " Test new nostop option + %d_ + let expected = "foo bar foobar" + call setline(1, expected) + call cursor(1, 8) + exe ":norm! ianotherone\<c-u>" + call assert_equal(expected, getline(1)) + call cursor(1, 8) + exe ":norm! ianothertwo\<c-w>" + call assert_equal(expected, getline(1)) + + let content = getline(1) + for value in ['indent,nostop', 'eol,nostop', 'indent,eol,nostop', 'indent,eol,start,nostop'] + exe ":set bs=".. value + %d _ + call setline(1, content) + let expected = " foobar" + call cursor(1, 8) + exe ":norm! ianotherone\<c-u>" + call assert_equal(expected, getline(1), 'CTRL-U backspace value: '.. &bs) + let expected = "foo foobar" + call setline(1, content) + call cursor(1, 8) + exe ":norm! ianothertwo\<c-w>" + call assert_equal(expected, getline(1), 'CTRL-W backspace value: '.. &bs) + endfor + + " Reset options + set compatible&vim + set visualbell&vim + set backspace&vim + close! +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_compiler.vim b/src/nvim/testdir/test_compiler.vim index 9101f8cfa0..d361205baa 100644 --- a/src/nvim/testdir/test_compiler.vim +++ b/src/nvim/testdir/test_compiler.vim @@ -37,17 +37,27 @@ func Test_compiler() bw! endfunc +func GetCompilerNames() + " return glob('$VIMRUNTIME/compiler/*.vim', 0, 1) + " \ ->map({i, v -> substitute(v, '.*[\\/]\([a-zA-Z0-9_\-]*\).vim', '\1', '')}) + " \ ->sort() + return sort(map(glob('$VIMRUNTIME/compiler/*.vim', 0, 1), {i, v -> substitute(v, '.*[\\/]\([a-zA-Z0-9_\-]*\).vim', '\1', '')})) +endfunc + func Test_compiler_without_arg() let runtime = substitute($VIMRUNTIME, '\\', '/', 'g') let a = split(execute('compiler')) - call assert_match(runtime .. '/compiler/ant.vim$', a[0]) - call assert_match(runtime .. '/compiler/bcc.vim$', a[1]) - call assert_match(runtime .. '/compiler/xo.vim$', a[-1]) + let exp = GetCompilerNames() + call assert_match(runtime .. '/compiler/' .. exp[0] .. '.vim$', a[0]) + call assert_match(runtime .. '/compiler/' .. exp[1] .. '.vim$', a[1]) + call assert_match(runtime .. '/compiler/' .. exp[-1] .. '.vim$', a[-1]) endfunc func Test_compiler_completion() + " let clist = GetCompilerNames()->join(' ') + let clist = join(GetCompilerNames(), ' ') call feedkeys(":compiler \<C-A>\<C-B>\"\<CR>", 'tx') - call assert_match('^"compiler ant bcc .* xmlwf xo$', @:) + call assert_match('^"compiler ' .. clist .. '$', @:) call feedkeys(":compiler p\<C-A>\<C-B>\"\<CR>", 'tx') call assert_equal('"compiler pbx perl php pylint pyunit', @:) diff --git a/src/nvim/testdir/test_eval_stuff.vim b/src/nvim/testdir/test_eval_stuff.vim index 061364fb73..ff50d53d86 100644 --- a/src/nvim/testdir/test_eval_stuff.vim +++ b/src/nvim/testdir/test_eval_stuff.vim @@ -22,6 +22,17 @@ func Test_E963() call assert_equal(v_o, v:oldfiles) endfunc +func Test_for_invalid() + call assert_fails("for x in 99", 'E714:') + call assert_fails("for x in 'asdf'", 'E714:') + call assert_fails("for x in {'a': 9}", 'E714:') + + if 0 + /1/5/2/s/\n + endif + redraw +endfunc + func Test_mkdir_p() call mkdir('Xmkdir/nested', 'p') call assert_true(isdirectory('Xmkdir/nested')) diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index 1cb68e7fef..f9f0ade1f6 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -159,6 +159,7 @@ let s:filename_checks = { \ 'elinks': ['elinks.conf'], \ 'elm': ['file.elm'], \ 'elmfilt': ['filter-rules'], + \ 'epuppet': ['file.epp'], \ 'erlang': ['file.erl', 'file.hrl', 'file.yaws'], \ 'eruby': ['file.erb', 'file.rhtml'], \ 'esmtprc': ['anyesmtprc'], @@ -171,6 +172,7 @@ let s:filename_checks = { \ 'factor': ['file.factor'], \ 'falcon': ['file.fal'], \ 'fan': ['file.fan', 'file.fwt'], + \ 'fennel': ['file.fnl'], \ 'fetchmail': ['.fetchmailrc'], \ 'fgl': ['file.4gl', 'file.4gh', 'file.m4gl'], \ 'focexec': ['file.fex', 'file.focexec'], @@ -332,7 +334,7 @@ let s:filename_checks = { \ 'pamconf': ['/etc/pam.conf'], \ 'pamenv': ['/etc/security/pam_env.conf', '/home/user/.pam_environment'], \ 'papp': ['file.papp', 'file.pxml', 'file.pxsl'], - \ 'pascal': ['file.pas', 'file.pp', 'file.dpr', 'file.lpr'], + \ 'pascal': ['file.pas', 'file.dpr', 'file.lpr'], \ 'passwd': ['any/etc/passwd', 'any/etc/passwd-', 'any/etc/passwd.edit', 'any/etc/shadow', 'any/etc/shadow-', 'any/etc/shadow.edit', 'any/var/backups/passwd.bak', 'any/var/backups/shadow.bak'], \ 'pbtxt': ['file.pbtxt'], \ 'pccts': ['file.g'], @@ -368,11 +370,13 @@ let s:filename_checks = { \ 'proto': ['file.proto'], \ 'protocols': ['/etc/protocols'], \ 'psf': ['file.psf'], + \ 'puppet': ['file.pp'], \ 'pyrex': ['file.pyx', 'file.pxd'], \ 'python': ['file.py', 'file.pyw', '.pythonstartup', '.pythonrc', 'file.ptl', 'file.pyi', 'SConstruct'], \ 'quake': ['anybaseq2/file.cfg', 'anyid1/file.cfg', 'quake3/file.cfg'], \ 'radiance': ['file.rad', 'file.mat'], \ 'ratpoison': ['.ratpoisonrc', 'ratpoisonrc'], + \ 'rbs': ['file.rbs'], \ 'rc': ['file.rc', 'file.rch'], \ 'rcs': ['file,v'], \ 'readline': ['.inputrc', 'inputrc'], @@ -389,7 +393,7 @@ let s:filename_checks = { \ 'rpl': ['file.rpl'], \ 'rst': ['file.rst'], \ 'rtf': ['file.rtf'], - \ 'ruby': ['.irbrc', 'irbrc', 'file.rb', 'file.rbw', 'file.gemspec', 'file.ru', 'Gemfile', 'file.builder', 'file.rxml', 'file.rjs', 'file.rant', 'file.rake'], + \ 'ruby': ['.irbrc', 'irbrc', 'file.rb', 'file.rbw', 'file.gemspec', 'file.ru', 'Gemfile', 'file.builder', 'file.rxml', 'file.rjs', 'file.rant', 'file.rake', 'rakefile', 'Rakefile', 'rantfile', 'Rantfile', 'rakefile-file', 'Rakefile-file', 'Puppetfile'], \ 'rust': ['file.rs'], \ 'samba': ['smb.conf'], \ 'sas': ['file.sas'], @@ -428,6 +432,7 @@ let s:filename_checks = { \ 'smith': ['file.smt', 'file.smith'], \ 'sml': ['file.sml'], \ 'snobol4': ['file.sno', 'file.spt'], + \ 'sparql': ['file.rq', 'file.sparql'], \ 'spec': ['file.spec'], \ 'spice': ['file.sp', 'file.spice'], \ 'spup': ['file.speedup', 'file.spdata', 'file.spd'], @@ -619,6 +624,8 @@ let s:script_checks = { \ 'cpp': [['// Standard iostream objects -*- C++ -*-'], \ ['// -*- C++ -*-']], \ 'yaml': [['%YAML 1.2']], + \ 'pascal': [['#!/path/instantfpc']], + \ 'fennel': [['#!/path/fennel']], \ } " Various forms of "env" optional arguments. @@ -689,5 +696,50 @@ func Test_ts_file() filetype off endfunc +func Test_ttl_file() + filetype on + + call writefile(['@base <http://example.org/> .'], 'Xfile.ttl') + split Xfile.ttl + call assert_equal('turtle', &filetype) + bwipe! + + call writefile(['looks like Tera Term Language'], 'Xfile.ttl') + split Xfile.ttl + call assert_equal('teraterm', &filetype) + bwipe! + + call delete('Xfile.ttl') + filetype off +endfunc + +func Test_pp_file() + filetype on + + call writefile(['looks like puppet'], 'Xfile.pp') + split Xfile.pp + call assert_equal('puppet', &filetype) + bwipe! + + let g:filetype_pp = 'pascal' + split Xfile.pp + call assert_equal('pascal', &filetype) + bwipe! + + " Test dist#ft#FTpp() + call writefile(['{ pascal comment'], 'Xfile.pp') + split Xfile.pp + call assert_equal('pascal', &filetype) + bwipe! + + call writefile(['procedure pascal'], 'Xfile.pp') + split Xfile.pp + call assert_equal('pascal', &filetype) + bwipe! + + call delete('Xfile.pp') + filetype off +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_fold.vim b/src/nvim/testdir/test_fold.vim index 88ce64b9eb..2d058e8e32 100644 --- a/src/nvim/testdir/test_fold.vim +++ b/src/nvim/testdir/test_fold.vim @@ -822,4 +822,39 @@ func Test_fold_create_delete() bwipe! endfunc +func Test_fold_relative_move() + enew! + set fdm=indent sw=2 wrap tw=80 + + let content = [ ' foo', ' bar', ' baz', + \ repeat('x', &columns + 1), + \ ' foo', ' bar', ' baz' + \ ] + call append(0, content) + + normal zM + + call cursor(3, 1) + call assert_true(foldclosed(line('.'))) + normal gj + call assert_equal(2, winline()) + + call cursor(2, 1) + call assert_true(foldclosed(line('.'))) + normal 2gj + call assert_equal(3, winline()) + + call cursor(5, 1) + call assert_true(foldclosed(line('.'))) + normal gk + call assert_equal(3, winline()) + + call cursor(6, 1) + call assert_true(foldclosed(line('.'))) + normal 2gk + call assert_equal(2, winline()) + + set fdm& sw& wrap& tw& +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_highlight.vim b/src/nvim/testdir/test_highlight.vim index 8f6834c2ab..4cc4d775d1 100644 --- a/src/nvim/testdir/test_highlight.vim +++ b/src/nvim/testdir/test_highlight.vim @@ -2,6 +2,7 @@ source view_util.vim source screendump.vim +source check.vim func Test_highlight() " basic test if ":highlight" doesn't crash @@ -610,3 +611,16 @@ func Test_1_highlight_Normalgroup_exists() call assert_match('hi Normal\s*font=.*', hlNormal) endif endfunc + +" Test for using RGB color values in a highlight group +func Test_xxlast_highlight_RGB_color() + CheckCanRunGui + gui -f + hi MySearch guifg=#110000 guibg=#001100 guisp=#000011 + call assert_equal('#110000', synIDattr(synIDtrans(hlID('MySearch')), 'fg#')) + call assert_equal('#001100', synIDattr(synIDtrans(hlID('MySearch')), 'bg#')) + call assert_equal('#000011', synIDattr(synIDtrans(hlID('MySearch')), 'sp#')) + hi clear +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_marks.vim b/src/nvim/testdir/test_marks.vim index e25fe33bb7..2fd82a4b6d 100644 --- a/src/nvim/testdir/test_marks.vim +++ b/src/nvim/testdir/test_marks.vim @@ -171,6 +171,11 @@ func Test_delmarks() " Deleting an already deleted mark should not fail. delmarks x + " getpos() should return all zeros after deleting a filemark. + norm mA + delmarks A + call assert_equal([0, 0, 0, 0], getpos("'A")) + " Test deleting a range of marks. norm ma norm mb diff --git a/src/nvim/testdir/test_mksession.vim b/src/nvim/testdir/test_mksession.vim index f71da92bf8..88320e0c22 100644 --- a/src/nvim/testdir/test_mksession.vim +++ b/src/nvim/testdir/test_mksession.vim @@ -291,6 +291,32 @@ endfunc endif +func Test_mkview_open_folds() + enew! + + call append(0, ['a', 'b', 'c']) + 1,3fold + " zR affects 'foldlevel', make sure the option is applied after the folds + " have been recreated. + normal zR + write! Xtestfile + + call assert_equal(-1, foldclosed(1)) + call assert_equal(-1, foldclosed(2)) + call assert_equal(-1, foldclosed(3)) + + mkview! Xtestview + source Xtestview + + call assert_equal(-1, foldclosed(1)) + call assert_equal(-1, foldclosed(2)) + call assert_equal(-1, foldclosed(3)) + + call delete('Xtestview') + call delete('Xtestfile') + %bwipe +endfunc + " Test :mkview with a file argument. func Test_mkview_file() " Create a view with line number and a fold. diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim index ccc5e6ffc9..5a10c9baa6 100644 --- a/src/nvim/testdir/test_options.vim +++ b/src/nvim/testdir/test_options.vim @@ -266,8 +266,14 @@ func Test_set_errors() call assert_fails('set foldmarker=x', 'E536:') call assert_fails('set commentstring=x', 'E537:') call assert_fails('set complete=x', 'E539:') + call assert_fails('set rulerformat=%-', 'E539:') + call assert_fails('set rulerformat=%(', 'E542:') + call assert_fails('set rulerformat=%15(%%', 'E542:') + call assert_fails('set statusline=%$', 'E539:') call assert_fails('set statusline=%{', 'E540:') call assert_fails('set statusline=%(', 'E542:') + call assert_fails('set statusline=%)', 'E542:') + if has('cursorshape') " This invalid value for 'guicursor' used to cause Vim to crash. call assert_fails('set guicursor=i-ci,r-cr:h', 'E545:') @@ -281,6 +287,21 @@ func Test_set_errors() call assert_fails('set winminwidth=10 winwidth=9', 'E592:') call assert_fails("set showbreak=\x01", 'E595:') call assert_fails('set t_foo=', 'E846:') + if has('python') || has('python3') + call assert_fails('set pyxversion=6', 'E474:') + endif + call assert_fails("let &tabstop='ab'", 'E521:') + call assert_fails('set sessionoptions=curdir,sesdir', 'E474:') + call assert_fails('set foldmarker={{{,', 'E474:') + call assert_fails('set sessionoptions=sesdir,curdir', 'E474:') + call assert_fails('set listchars=trail:· ambiwidth=double', 'E834:') + set listchars& + call assert_fails('set fillchars=stl:· ambiwidth=double', 'E835:') + set fillchars& + call assert_fails('set fileencoding=latin1,utf-8', 'E474:') + set nomodifiable + call assert_fails('set fileencoding=latin1', 'E21:') + set modifiable& endfunc " Must be executed before other tests that set 'term'. @@ -309,12 +330,10 @@ func Test_set_ttytype() set ttytype=xterm call assert_equal('xterm', &ttytype) call assert_equal(&ttytype, &term) - " "set ttytype=" gives E522 instead of E529 - " in travis on some builds. Why? Catch both for now try set ttytype= call assert_report('set ttytype= did not fail') - catch /E529\|E522/ + catch /E529/ endtry " Some systems accept any terminal name and return dumb settings, diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index 48c0a83053..704fdacdcd 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -2650,6 +2650,13 @@ func Test_vimgrep() call XvimgrepTests('l') endfunc +func Test_vimgrep_wildcards_expanded_once() + new X[id-01] file.txt + call setline(1, 'some text to search for') + vimgrep text % + bwipe! +endfunc + " Test for incsearch highlighting of the :vimgrep pattern " This test used to cause "E315: ml_get: invalid lnum" errors. func Test_vimgrep_incsearch() @@ -3540,7 +3547,7 @@ func Test_lbuffer_crash() sv Xtest augroup QF_Test au! - au * * bw + au QuickFixCmdPre,QuickFixCmdPost,BufEnter,BufLeave * bw augroup END lbuffer augroup QF_Test @@ -3552,7 +3559,7 @@ endfunc func Test_lexpr_crash() augroup QF_Test au! - au * * call setloclist(0, [], 'f') + au QuickFixCmdPre,QuickFixCmdPost,BufEnter,BufLeave * call setloclist(0, [], 'f') augroup END lexpr "" augroup QF_Test @@ -3587,7 +3594,7 @@ func Test_lvimgrep_crash() sv Xtest augroup QF_Test au! - au * * call setloclist(0, [], 'f') + au QuickFixCmdPre,QuickFixCmdPost,BufEnter,BufLeave * call setloclist(0, [], 'f') augroup END lvimgrep quickfix test_quickfix.vim augroup QF_Test @@ -3889,7 +3896,7 @@ func Test_lbuffer_with_bwipe() new new augroup nasty - au * * bwipe + au QuickFixCmdPre,QuickFixCmdPost,BufEnter,BufLeave * bwipe augroup END lbuffer augroup nasty @@ -3902,9 +3909,9 @@ endfunc func Xexpr_acmd_freelist(cchar) call s:setup_commands(a:cchar) - " This was using freed memory. + " This was using freed memory (but with what events?) augroup nasty - au * * call g:Xsetlist([], 'f') + au QuickFixCmdPre,QuickFixCmdPost,BufEnter,BufLeave * call g:Xsetlist([], 'f') augroup END Xexpr "x" augroup nasty diff --git a/src/nvim/testdir/test_sleep.vim b/src/nvim/testdir/test_sleep.vim new file mode 100644 index 0000000000..f71855fd4b --- /dev/null +++ b/src/nvim/testdir/test_sleep.vim @@ -0,0 +1,26 @@ +" Test for sleep and sleep! commands + +func! s:get_time_ms() + let timestr = reltimestr(reltime()) + let dotidx = stridx(timestr, '.') + let sec = str2nr(timestr[:dotidx]) + let msec = str2nr(timestr[dotidx + 1:]) + return (sec * 1000) + (msec / 1000) +endfunc + +func! s:assert_takes_longer(cmd, time_ms) + let start = s:get_time_ms() + execute a:cmd + let end = s:get_time_ms() + call assert_true(end - start >=# a:time_ms) +endfun + +func! Test_sleep_bang() + call s:assert_takes_longer('sleep 50m', 50) + call s:assert_takes_longer('sleep! 50m', 50) + call s:assert_takes_longer('sl 50m', 50) + call s:assert_takes_longer('sl! 50m', 50) + call s:assert_takes_longer('1sleep', 1000) +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_startup.vim b/src/nvim/testdir/test_startup.vim index e7f332bc4c..e6ad92f483 100644 --- a/src/nvim/testdir/test_startup.vim +++ b/src/nvim/testdir/test_startup.vim @@ -735,4 +735,82 @@ func Test_x_arg() call delete('Xtest_x_arg') endfunc +" Test starting vim with various names: vim, ex, view, evim, etc. +func Test_progname() + CheckUnix + + call mkdir('Xprogname', 'p') + call writefile(['silent !date', + \ 'call writefile([mode(1), ' + \ .. '&insertmode, &diff, &readonly, &updatecount, ' + \ .. 'join(split(execute("message"), "\n")[1:])], "Xprogname_out")', + \ 'qall'], 'Xprogname_after') + + " +---------------------------------------------- progname + " | +--------------------------------- mode(1) + " | | +--------------------------- &insertmode + " | | | +---------------------- &diff + " | | | | +----------------- &readonly + " | | | | | +-------- &updatecount + " | | | | | | +--- :messages + " | | | | | | | + " let expectations = { + " \ 'vim': ['n', '0', '0', '0', '200', ''], + " \ 'gvim': ['n', '0', '0', '0', '200', ''], + " \ 'ex': ['ce', '0', '0', '0', '200', ''], + " \ 'exim': ['cv', '0', '0', '0', '200', ''], + " \ 'view': ['n', '0', '0', '1', '10000', ''], + " \ 'gview': ['n', '0', '0', '1', '10000', ''], + " \ 'evim': ['n', '1', '0', '0', '200', ''], + " \ 'eview': ['n', '1', '0', '1', '10000', ''], + " \ 'rvim': ['n', '0', '0', '0', '200', 'line 1: E145: Shell commands and some functionality not allowed in rvim'], + " \ 'rgvim': ['n', '0', '0', '0', '200', 'line 1: E145: Shell commands and some functionality not allowed in rvim'], + " \ 'rview': ['n', '0', '0', '1', '10000', 'line 1: E145: Shell commands and some functionality not allowed in rvim'], + " \ 'rgview': ['n', '0', '0', '1', '10000', 'line 1: E145: Shell commands and some functionality not allowed in rvim'], + " \ 'vimdiff': ['n', '0', '1', '0', '200', ''], + " \ 'gvimdiff': ['n', '0', '1', '0', '200', '']} + let expectations = {'nvim': ['n', '0', '0', '0', '200', '']} + + " let prognames = ['vim', 'gvim', 'ex', 'exim', 'view', 'gview', + " \ 'evim', 'eview', 'rvim', 'rgvim', 'rview', 'rgview', + " \ 'vimdiff', 'gvimdiff'] + let prognames = ['nvim'] + + for progname in prognames + if empty($DISPLAY) + if progname =~# 'g' + " Can't run gvim, gview (etc.) if $DISPLAY is not setup. + continue + endif + if has('gui') && (progname ==# 'evim' || progname ==# 'eview') + " evim or eview will start the GUI if there is gui support. + " So don't try to start them either if $DISPLAY is not setup. + continue + endif + endif + + exe 'silent !ln -s -f ' ..exepath(GetVimProg()) .. ' Xprogname/' .. progname + + let stdout_stderr = '' + if progname =~# 'g' + let stdout_stderr = system('Xprogname/'..progname..' -f --clean --not-a-term -S Xprogname_after') + else + exe 'sil !Xprogname/'..progname..' -f --clean -S Xprogname_after' + endif + + if progname =~# 'g' && !has('gui') + call assert_equal("E25: GUI cannot be used: Not enabled at compile time\n", stdout_stderr, progname) + else + call assert_equal('', stdout_stderr, progname) + call assert_equal(expectations[progname], readfile('Xprogname_out'), progname) + endif + + call delete('Xprogname/' .. progname) + call delete('Xprogname_out') + endfor + + call delete('Xprogname_after') + call delete('Xprogname', 'd') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim index 687b1cb989..969b75d424 100644 --- a/src/nvim/testdir/test_window_cmd.vim +++ b/src/nvim/testdir/test_window_cmd.vim @@ -513,8 +513,8 @@ func Test_window_colon_command() endfunc func Test_access_freed_mem() - " This was accessing freed memory - au * 0 vs xxx + " This was accessing freed memory (but with what events?) + au BufEnter,BufLeave,WinEnter,WinLeave 0 vs xxx arg 0 argadd all diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index 124f96e039..3e683a4926 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -297,7 +297,7 @@ static void forward_mouse_event(TermInput *input, TermKeyKey *key) len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Release"); break; case TERMKEY_MOUSE_UNKNOWN: - assert(false); + abort(); } len += (size_t)snprintf(buf + len, sizeof(buf) - len, "><%d,%d>", col, row); diff --git a/src/nvim/ui_compositor.c b/src/nvim/ui_compositor.c index 9d3ec21949..06efc9fa99 100644 --- a/src/nvim/ui_compositor.c +++ b/src/nvim/ui_compositor.c @@ -157,7 +157,7 @@ bool ui_comp_put_grid(ScreenGrid *grid, int row, int col, int height, int width, #ifndef NDEBUG for (size_t i = 0; i < kv_size(layers); i++) { if (kv_A(layers, i) == grid) { - assert(false); + abort(); } } #endif diff --git a/src/nvim/version.c b/src/nvim/version.c index 834d27cc84..deba3f6e49 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -300,7 +300,7 @@ static const int included_patches[] = { 1620, 1619, 1618, - // 1617, + 1617, // 1616, 1615, 1614, @@ -392,7 +392,7 @@ static const int included_patches[] = { 1528, 1527, 1526, - // 1525, + 1525, 1524, 1523, 1522, @@ -462,7 +462,7 @@ static const int included_patches[] = { 1458, 1457, 1456, - // 1455, + 1455, 1454, 1453, 1452, diff --git a/src/nvim/vim.h b/src/nvim/vim.h index 01f20cf29a..e70749795b 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -159,6 +159,7 @@ enum { EXPAND_MAPCLEAR, EXPAND_ARGLIST, EXPAND_CHECKHEALTH, + EXPAND_LUA, }; diff --git a/src/nvim/viml/parser/expressions.c b/src/nvim/viml/parser/expressions.c index 44b6ab5f5a..e9d82ca87d 100644 --- a/src/nvim/viml/parser/expressions.c +++ b/src/nvim/viml/parser/expressions.c @@ -2078,7 +2078,7 @@ viml_pexpr_parse_process_token: case kExprLexMissing: case kExprLexSpacing: case kExprLexEOC: { - assert(false); + abort(); } case kExprLexInvalid: { ERROR_FROM_TOKEN(cur_token); @@ -3028,7 +3028,7 @@ viml_pexpr_parse_end: // Until trailing "}" it is impossible to distinguish curly braces // identifier and dictionary, so it must not appear in the stack like // this. - assert(false); + abort(); } case kExprNodeInteger: case kExprNodeFloat: @@ -3042,7 +3042,7 @@ viml_pexpr_parse_end: // These are plain values and not containers, for them it should only // be possible to show up in the topmost stack element, but it was // unconditionally popped at the start. - assert(false); + abort(); } case kExprNodeComma: case kExprNodeColon: |