diff options
Diffstat (limited to 'src')
50 files changed, 1164 insertions, 805 deletions
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 606baff619..2d803792c8 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -359,6 +359,10 @@ endforeach() # Our dependencies come first. +if (CMAKE_SYSTEM_NAME MATCHES "OpenBSD") + list(APPEND NVIM_LINK_LIBRARIES pthread c++abi) +endif() + if (LibIntl_FOUND) list(APPEND NVIM_LINK_LIBRARIES ${LibIntl_LIBRARY}) endif() @@ -437,6 +441,7 @@ if(WIN32) COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/tidy.exe" ${PROJECT_BINARY_DIR}/windows_runtime_deps/ COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/win32yank.exe" ${PROJECT_BINARY_DIR}/windows_runtime_deps/ COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/winpty-agent.exe" ${PROJECT_BINARY_DIR}/windows_runtime_deps/ + COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/xxd.exe" ${PROJECT_BINARY_DIR}/windows_runtime_deps/ COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/D3Dcompiler_47.dll" ${PROJECT_BINARY_DIR}/windows_runtime_deps/ COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/libEGL.dll" ${PROJECT_BINARY_DIR}/windows_runtime_deps/ diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 1115faaeaf..12a4279dd7 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -47,13 +47,15 @@ typedef struct { /// @param[out] tstate Location where try state should be saved. void try_enter(TryState *const tstate) { + // TODO(ZyX-I): Check whether try_enter()/try_leave() may use + // enter_cleanup()/leave_cleanup(). Or + // save_dbg_stuff()/restore_dbg_stuff(). *tstate = (TryState) { .current_exception = current_exception, .msg_list = (const struct msglist *const *)msg_list, .private_msg_list = NULL, .trylevel = trylevel, .got_int = got_int, - .did_throw = did_throw, .need_rethrow = need_rethrow, .did_emsg = did_emsg, }; @@ -61,7 +63,6 @@ void try_enter(TryState *const tstate) current_exception = NULL; trylevel = 1; got_int = false; - did_throw = false; need_rethrow = false; did_emsg = false; } @@ -82,7 +83,6 @@ bool try_leave(const TryState *const tstate, Error *const err) assert(trylevel == 0); assert(!need_rethrow); assert(!got_int); - assert(!did_throw); assert(!did_emsg); assert(msg_list == &tstate->private_msg_list); assert(*msg_list == NULL); @@ -91,7 +91,6 @@ bool try_leave(const TryState *const tstate, Error *const err) current_exception = tstate->current_exception; trylevel = tstate->trylevel; got_int = tstate->got_int; - did_throw = tstate->did_throw; need_rethrow = tstate->need_rethrow; did_emsg = tstate->did_emsg; return ret; @@ -127,7 +126,7 @@ bool try_end(Error *err) did_emsg = false; if (got_int) { - if (did_throw) { + if (current_exception) { // If we got an interrupt, discard the current exception discard_current_exception(); } @@ -146,7 +145,7 @@ bool try_end(Error *err) if (should_free) { xfree(msg); } - } else if (did_throw) { + } else if (current_exception) { api_set_error(err, kErrorTypeException, "%s", current_exception->value); discard_current_exception(); } diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index 87f334ac30..0634764f13 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -93,7 +93,6 @@ typedef struct { const struct msglist *const *msg_list; int trylevel; int got_int; - int did_throw; int need_rethrow; int did_emsg; } TryState; diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 4870c3fb8a..4cd2657561 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -56,7 +56,8 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY { if (pmap_has(uint64_t)(connected_uis, channel_id)) { - api_set_error(err, kErrorTypeException, "UI already attached for channel"); + api_set_error(err, kErrorTypeException, + "UI already attached to channel: %" PRId64, channel_id); return; } @@ -130,7 +131,8 @@ void nvim_ui_detach(uint64_t channel_id, Error *err) FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY { if (!pmap_has(uint64_t)(connected_uis, channel_id)) { - api_set_error(err, kErrorTypeException, "UI is not attached for channel"); + api_set_error(err, kErrorTypeException, + "UI not attached to channel: %" PRId64, channel_id); return; } remote_ui_disconnect(channel_id); @@ -142,7 +144,8 @@ void nvim_ui_try_resize(uint64_t channel_id, Integer width, FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY { if (!pmap_has(uint64_t)(connected_uis, channel_id)) { - api_set_error(err, kErrorTypeException, "UI is not attached for channel"); + api_set_error(err, kErrorTypeException, + "UI not attached to channel: %" PRId64, channel_id); return; } @@ -163,7 +166,8 @@ void nvim_ui_set_option(uint64_t channel_id, String name, FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY { if (!pmap_has(uint64_t)(connected_uis, channel_id)) { - api_set_error(error, kErrorTypeException, "UI is not attached for channel"); + api_set_error(error, kErrorTypeException, + "UI not attached to channel: %" PRId64, channel_id); return; } UI *ui = pmap_get(uint64_t)(connected_uis, channel_id); @@ -209,7 +213,8 @@ static void ui_set_option(UI *ui, String name, Object value, Error *error) return; } - api_set_error(error, kErrorTypeValidation, "No such ui option"); + api_set_error(error, kErrorTypeValidation, "No such UI option: %s", + name.data); #undef UI_EXT_OPTION } diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 962081cc23..07ec6e8c27 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -239,15 +239,17 @@ String nvim_command_output(String command, Error *err) } if (capture_local.ga_len > 1) { - // redir always(?) prepends a newline; remove it. - char *s = capture_local.ga_data; - assert(s[0] == '\n'); - memmove(s, s + 1, (size_t)capture_local.ga_len); - s[capture_local.ga_len - 1] = '\0'; - return (String) { // Caller will free the memory. - .data = s, - .size = (size_t)(capture_local.ga_len - 1), + String s = (String){ + .data = capture_local.ga_data, + .size = (size_t)capture_local.ga_len, }; + // redir usually (except :echon) prepends a newline. + if (s.data[0] == '\n') { + memmove(s.data, s.data + 1, s.size); + s.data[s.size - 1] = '\0'; + s.size = s.size - 1; + } + return s; // Caller will free the memory. } theend: diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua index 7dfaf54ff0..1153314e76 100644 --- a/src/nvim/auevents.lua +++ b/src/nvim/auevents.lua @@ -92,6 +92,8 @@ return { 'VimLeave', -- before exiting Vim 'VimLeavePre', -- before exiting Vim and writing ShaDa file 'VimResized', -- after Vim window was resized + 'VimResume', -- after Nvim is resumed + 'VimSuspend', -- before Nvim is suspended 'WinNew', -- when entering a new window 'WinEnter', -- after entering a window 'WinLeave', -- before leaving a window diff --git a/src/nvim/channel.c b/src/nvim/channel.c index 2e32af2e9a..776e2bfa86 100644 --- a/src/nvim/channel.c +++ b/src/nvim/channel.c @@ -237,15 +237,16 @@ void channel_create_event(Channel *chan, const char *ext_source) #endif } -void channel_incref(Channel *channel) +void channel_incref(Channel *chan) { - channel->refcount++; + chan->refcount++; } -void channel_decref(Channel *channel) +void channel_decref(Channel *chan) { - if (!(--channel->refcount)) { - multiqueue_put(main_loop.fast_events, free_channel_event, 1, channel); + if (!(--chan->refcount)) { + // delay free, so that libuv is done with the handles + multiqueue_put(main_loop.events, free_channel_event, 1, chan); } } @@ -267,18 +268,18 @@ void callback_reader_start(CallbackReader *reader) static void free_channel_event(void **argv) { - Channel *channel = argv[0]; - if (channel->is_rpc) { - rpc_free(channel); + Channel *chan = argv[0]; + if (chan->is_rpc) { + rpc_free(chan); } - callback_reader_free(&channel->on_stdout); - callback_reader_free(&channel->on_stderr); - callback_free(&channel->on_exit); + callback_reader_free(&chan->on_stdout); + callback_reader_free(&chan->on_stderr); + callback_free(&chan->on_exit); - pmap_del(uint64_t)(channels, channel->id); - multiqueue_free(channel->events); - xfree(channel); + pmap_del(uint64_t)(channels, chan->id); + multiqueue_free(chan->events); + xfree(chan); } static void channel_destroy_early(Channel *chan) @@ -286,12 +287,15 @@ static void channel_destroy_early(Channel *chan) if ((chan->id != --next_chan_id)) { abort(); } + pmap_del(uint64_t)(channels, chan->id); + chan->id = 0; if ((--chan->refcount != 0)) { abort(); } - free_channel_event((void **)&chan); + // uv will keep a reference to handles until next loop tick, so delay free + multiqueue_put(main_loop.events, free_channel_event, 1, chan); } diff --git a/src/nvim/edit.c b/src/nvim/edit.c index b772a944f4..a0f6ce152b 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -847,7 +847,7 @@ static int insert_handle_key(InsertState *s) case ' ': - if (mod_mask != 4) { + if (mod_mask != MOD_MASK_CTRL) { goto normalchar; } // FALLTHROUGH @@ -1180,6 +1180,14 @@ static int insert_handle_key(InsertState *s) normalchar: // Insert a normal character. + + if (mod_mask == MOD_MASK_ALT || mod_mask == MOD_MASK_META) { + // Unmapped ALT/META chord behaves like ESC+c. #8213 + stuffcharReadbuff(ESC); + stuffcharReadbuff(s->c); + break; + } + if (!p_paste) { // Trigger InsertCharPre. char_u *str = do_insert_char_pre(s->c); @@ -1432,7 +1440,7 @@ static void ins_ctrl_v(void) * line and will not removed by the redraw */ edit_unputchar(); clear_showcmd(); - insert_special(c, FALSE, TRUE); + insert_special(c, true, true); revins_chars++; revins_legal++; } @@ -3615,6 +3623,9 @@ int ins_compl_add_tv(typval_T *const tv, const Direction dir) memset(cptext, 0, sizeof(cptext)); } if (word == NULL || (!aempty && *word == NUL)) { + for (size_t i = 0; i < CPT_COUNT; i++) { + xfree(cptext[i]); + } return FAIL; } return ins_compl_add((char_u *)word, -1, icase, NULL, @@ -5054,13 +5065,11 @@ static void insert_special(int c, int allow_modmask, int ctrlv) char_u *p; int len; - /* - * Special function key, translate into "<Key>". Up to the last '>' is - * inserted with ins_str(), so as not to replace characters in replace - * mode. - * Only use mod_mask for special keys, to avoid things like <S-Space>, - * unless 'allow_modmask' is TRUE. - */ + // Special function key, translate into "<Key>". Up to the last '>' is + // inserted with ins_str(), so as not to replace characters in replace + // mode. + // Only use mod_mask for special keys, to avoid things like <S-Space>, + // unless 'allow_modmask' is TRUE. if (mod_mask & MOD_MASK_CMD) { // Command-key never produces a normal key. allow_modmask = true; } diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 0f7a1eb004..713eb816f8 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -14403,8 +14403,11 @@ static void f_serverstop(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = 0; if (argvars[0].vval.v_string) { - server_stop((char *) argvars[0].vval.v_string); + bool rv = server_stop((char *)argvars[0].vval.v_string); + rettv->vval.v_number = (rv ? 1 : 0); } } @@ -15699,6 +15702,56 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) p_cpo = save_cpo; } +/// "stdpath()" helper for list results +static void get_xdg_var_list(const XDGVarType xdg, typval_T *rettv) + FUNC_ATTR_NONNULL_ALL +{ + const void *iter = NULL; + list_T *const list = tv_list_alloc(kListLenShouldKnow); + rettv->v_type = VAR_LIST; + rettv->vval.v_list = list; + tv_list_ref(list); + char *const dirs = stdpaths_get_xdg_var(xdg); + do { + size_t dir_len; + const char *dir; + iter = vim_env_iter(':', dirs, iter, &dir, &dir_len); + if (dir != NULL && dir_len > 0) { + char *dir_with_nvim = xmemdupz(dir, dir_len); + dir_with_nvim = concat_fnames_realloc(dir_with_nvim, "nvim", true); + tv_list_append_string(list, dir_with_nvim, strlen(dir_with_nvim)); + xfree(dir_with_nvim); + } + } while (iter != NULL); + xfree(dirs); +} + +/// "stdpath(type)" function +static void f_stdpath(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + + const char *const p = tv_get_string_chk(&argvars[0]); + if (p == NULL) { + return; // Type error; errmsg already given. + } + + if (strcmp(p, "config") == 0) { + rettv->vval.v_string = (char_u *)get_xdg_home(kXDGConfigHome); + } else if (strcmp(p, "data") == 0) { + rettv->vval.v_string = (char_u *)get_xdg_home(kXDGDataHome); + } else if (strcmp(p, "cache") == 0) { + rettv->vval.v_string = (char_u *)get_xdg_home(kXDGCacheHome); + } else if (strcmp(p, "config_dirs") == 0) { + get_xdg_var_list(kXDGConfigDirs, rettv); + } else if (strcmp(p, "data_dirs") == 0) { + get_xdg_var_list(kXDGDataDirs, rettv); + } else { + EMSG2(_("E6100: \"%s\" is not a valid stdpath"), p); + } +} + /* * "str2float()" function */ @@ -16861,6 +16914,12 @@ static void f_timer_pause(typval_T *argvars, typval_T *unused, FunPtr fptr) int paused = (bool)tv_get_number(&argvars[1]); timer_T *timer = pmap_get(uint64_t)(timers, tv_get_number(&argvars[0])); if (timer != NULL) { + if (!timer->paused && paused) { + time_watcher_stop(&timer->tw); + } else if (timer->paused && !paused) { + time_watcher_start(&timer->tw, timer_due_cb, timer->timeout, + timer->timeout); + } timer->paused = paused; } } diff --git a/src/nvim/eval.h b/src/nvim/eval.h index b798eae187..149dae688e 100644 --- a/src/nvim/eval.h +++ b/src/nvim/eval.h @@ -10,6 +10,7 @@ #include "nvim/event/rstream.h" #include "nvim/event/wstream.h" #include "nvim/channel.h" +#include "nvim/os/stdpaths_defs.h" #define COPYID_INC 2 #define COPYID_MASK (~0x1) diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index daa3b637a3..801d2cc468 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -280,6 +280,7 @@ return { spellsuggest={args={1, 3}}, split={args={1, 3}}, sqrt={args=1, func="float_op_wrapper", data="&sqrt"}, + stdpath={args=1}, str2float={args=1}, str2nr={args={1, 2}}, strcharpart={args={2, 3}}, diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 99495aaa61..7cd6dbdeca 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -133,7 +133,6 @@ struct dbg_stuff { char_u *vv_throwpoint; int did_emsg; int got_int; - int did_throw; int need_rethrow; int check_cstack; except_T *current_exception; @@ -165,12 +164,11 @@ static void save_dbg_stuff(struct dbg_stuff *dsp) dsp->vv_exception = v_exception(NULL); dsp->vv_throwpoint = v_throwpoint(NULL); - /* Necessary for debugging an inactive ":catch", ":finally", ":endtry" */ - dsp->did_emsg = did_emsg; did_emsg = FALSE; - dsp->got_int = got_int; got_int = FALSE; - dsp->did_throw = did_throw; did_throw = FALSE; - dsp->need_rethrow = need_rethrow; need_rethrow = FALSE; - dsp->check_cstack = check_cstack; check_cstack = FALSE; + // Necessary for debugging an inactive ":catch", ":finally", ":endtry". + dsp->did_emsg = did_emsg; did_emsg = false; + dsp->got_int = got_int; got_int = false; + dsp->need_rethrow = need_rethrow; need_rethrow = false; + dsp->check_cstack = check_cstack; check_cstack = false; dsp->current_exception = current_exception; current_exception = NULL; } @@ -184,7 +182,6 @@ static void restore_dbg_stuff(struct dbg_stuff *dsp) (void)v_throwpoint(dsp->vv_throwpoint); did_emsg = dsp->did_emsg; got_int = dsp->got_int; - did_throw = dsp->did_throw; need_rethrow = dsp->need_rethrow; check_cstack = dsp->check_cstack; current_exception = dsp->current_exception; @@ -400,16 +397,11 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, initial_trylevel = trylevel; - /* - * "did_throw" will be set to TRUE when an exception is being thrown. - */ - did_throw = FALSE; - /* - * "did_emsg" will be set to TRUE when emsg() is used, in which case we - * cancel the whole command line, and any if/endif or loop. - * If force_abort is set, we cancel everything. - */ - did_emsg = FALSE; + current_exception = NULL; + // "did_emsg" will be set to TRUE when emsg() is used, in which case we + // cancel the whole command line, and any if/endif or loop. + // If force_abort is set, we cancel everything. + did_emsg = false; /* * KeyTyped is only set when calling vgetc(). Reset it here when not @@ -659,7 +651,7 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, * not to use a cs_line[] from an entry that isn't a ":while" * or ":for": It would make "current_line" invalid and can * cause a crash. */ - if (!did_emsg && !got_int && !did_throw + if (!did_emsg && !got_int && !current_exception && cstack.cs_idx >= 0 && (cstack.cs_flags[cstack.cs_idx] & (CSF_WHILE | CSF_FOR)) @@ -707,7 +699,7 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, } /* - * A ":finally" makes did_emsg, got_int, and did_throw pending for + * A ":finally" makes did_emsg, got_int and current_exception pending for * being restored at the ":endtry". Reset them here and set the * ACTIVE and FINALLY flags, so that the finally clause gets executed. * This includes the case where a missing ":endif", ":endwhile" or @@ -715,10 +707,11 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, */ if (cstack.cs_lflags & CSL_HAD_FINA) { cstack.cs_lflags &= ~CSL_HAD_FINA; - report_make_pending(cstack.cs_pending[cstack.cs_idx] - & (CSTP_ERROR | CSTP_INTERRUPT | CSTP_THROW), - did_throw ? (void *)current_exception : NULL); - did_emsg = got_int = did_throw = FALSE; + report_make_pending((cstack.cs_pending[cstack.cs_idx] + & (CSTP_ERROR | CSTP_INTERRUPT | CSTP_THROW)), + current_exception); + did_emsg = got_int = false; + current_exception = NULL; cstack.cs_flags[cstack.cs_idx] |= CSF_ACTIVE | CSF_FINALLY; } @@ -726,15 +719,14 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, * within this loop. */ trylevel = initial_trylevel + cstack.cs_trylevel; - /* - * If the outermost try conditional (across function calls and sourced - * files) is aborted because of an error, an interrupt, or an uncaught - * exception, cancel everything. If it is left normally, reset - * force_abort to get the non-EH compatible abortion behavior for - * the rest of the script. - */ - if (trylevel == 0 && !did_emsg && !got_int && !did_throw) - force_abort = FALSE; + // If the outermost try conditional (across function calls and sourced + // files) is aborted because of an error, an interrupt, or an uncaught + // exception, cancel everything. If it is left normally, reset + // force_abort to get the non-EH compatible abortion behavior for + // the rest of the script. + if (trylevel == 0 && !did_emsg && !got_int && !current_exception) { + force_abort = false; + } /* Convert an interrupt to an exception if appropriate. */ (void)do_intthrow(&cstack); @@ -749,11 +741,8 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, * - there is a command after '|', inside a :if, :while, :for or :try, or * looping for ":source" command or function call. */ - while (!((got_int - || (did_emsg && force_abort) || did_throw - ) - && cstack.cs_trylevel == 0 - ) + while (!((got_int || (did_emsg && force_abort) || current_exception) + && cstack.cs_trylevel == 0) && !(did_emsg /* Keep going when inside try/catch, so that the error can be * deal with, except when it is a syntax error, it may cause @@ -775,7 +764,7 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, * If a sourced file or executed function ran to its end, report the * unclosed conditional. */ - if (!got_int && !did_throw + if (!got_int && !current_exception && ((getline_equal(fgetline, cookie, getsourceline) && !source_finished(fgetline, cookie)) || (getline_equal(fgetline, cookie, get_func_line) @@ -815,17 +804,16 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, ? (char_u *)"endfunction" : (char_u *)NULL); if (trylevel == 0) { - /* - * When an exception is being thrown out of the outermost try - * conditional, discard the uncaught exception, disable the conversion - * of interrupts or errors to exceptions, and ensure that no more - * commands are executed. - */ - if (did_throw) { - void *p = NULL; - char_u *saved_sourcing_name; + // When an exception is being thrown out of the outermost try + // conditional, discard the uncaught exception, disable the conversion + // of interrupts or errors to exceptions, and ensure that no more + // commands are executed. + if (current_exception) { + void *p = NULL; + char_u *saved_sourcing_name; int saved_sourcing_lnum; - struct msglist *messages = NULL, *next; + struct msglist *messages = NULL; + struct msglist *next; /* * If the uncaught exception is a user exception, report it as an @@ -885,22 +873,22 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, suppress_errthrow = TRUE; } - /* - * The current cstack will be freed when do_cmdline() returns. An uncaught - * exception will have to be rethrown in the previous cstack. If a function - * has just returned or a script file was just finished and the previous - * cstack belongs to the same function or, respectively, script file, it - * will have to be checked for finally clauses to be executed due to the - * ":return" or ":finish". This is done in do_one_cmd(). - */ - if (did_throw) - need_rethrow = TRUE; + // The current cstack will be freed when do_cmdline() returns. An uncaught + // exception will have to be rethrown in the previous cstack. If a function + // has just returned or a script file was just finished and the previous + // cstack belongs to the same function or, respectively, script file, it + // will have to be checked for finally clauses to be executed due to the + // ":return" or ":finish". This is done in do_one_cmd(). + if (current_exception) { + need_rethrow = true; + } if ((getline_equal(fgetline, cookie, getsourceline) && ex_nesting_level > source_level(real_cookie)) || (getline_equal(fgetline, cookie, get_func_line) && ex_nesting_level > func_level(real_cookie) + 1)) { - if (!did_throw) - check_cstack = TRUE; + if (!current_exception) { + check_cstack = true; + } } else { /* When leaving a function, reduce nesting level. */ if (getline_equal(fgetline, cookie, get_func_line)) @@ -1480,10 +1468,11 @@ static char_u * do_one_cmd(char_u **cmdlinep, } char_u *after_modifier = ea.cmd; - ea.skip = did_emsg || got_int || did_throw || (cstack->cs_idx >= 0 - && !(cstack->cs_flags[cstack-> - cs_idx] - & CSF_ACTIVE)); + ea.skip = (did_emsg + || got_int + || current_exception + || (cstack->cs_idx >= 0 + && !(cstack->cs_flags[cstack->cs_idx] & CSF_ACTIVE))); /* Count this line for profiling if ea.skip is FALSE. */ if (do_profiling == PROF_YES && !ea.skip) { @@ -1782,13 +1771,14 @@ static char_u * do_one_cmd(char_u **cmdlinep, )); - /* forced commands */ + // Forced commands. if (*p == '!' && ea.cmdidx != CMD_substitute && ea.cmdidx != CMD_smagic && ea.cmdidx != CMD_snomagic) { - ++p; - ea.forceit = TRUE; - } else - ea.forceit = FALSE; + p++; + ea.forceit = true; + } else { + ea.forceit = false; + } /* * 6. Parse arguments. @@ -6310,15 +6300,18 @@ static void ex_stop(exarg_T *eap) if (!eap->forceit) { autowrite_all(); } + apply_autocmds(EVENT_VIMSUSPEND, NULL, NULL, false, NULL); ui_cursor_goto((int)Rows - 1, 0); ui_linefeed(); ui_flush(); ui_call_suspend(); // call machine specific function + ui_flush(); maketitle(); resettitle(); // force updating the title redraw_later_clear(); ui_refresh(); // may have resized window + apply_autocmds(EVENT_VIMRESUME, NULL, NULL, false, NULL); } } @@ -6985,12 +6978,10 @@ do_exedit ( ex_no_reprint = TRUE; } -/* - * ":gui" and ":gvim" when there is no GUI. - */ +/// ":gui" and ":gvim" when there is no GUI. static void ex_nogui(exarg_T *eap) { - eap->errmsg = e_nogvim; + eap->errmsg = (char_u *)N_("E25: Nvim does not have a built-in GUI"); } diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c index 4010a088c8..e23945c842 100644 --- a/src/nvim/ex_eval.c +++ b/src/nvim/ex_eval.c @@ -45,22 +45,21 @@ * there or even outside the try conditional. Try conditionals may be nested. */ -/* - * Configuration whether an exception is thrown on error or interrupt. When - * the preprocessor macros below evaluate to FALSE, an error (did_emsg) or - * interrupt (got_int) under an active try conditional terminates the script - * after the non-active finally clauses of all active try conditionals have been - * executed. Otherwise, errors and/or interrupts are converted into catchable - * exceptions (did_throw additionally set), which terminate the script only if - * not caught. For user exceptions, only did_throw is set. (Note: got_int can - * be set asynchronously afterwards by a SIGINT, so did_throw && got_int is not - * a reliant test that the exception currently being thrown is an interrupt - * exception. Similarly, did_emsg can be set afterwards on an error in an - * (unskipped) conditional command inside an inactive conditional, so did_throw - * && did_emsg is not a reliant test that the exception currently being thrown - * is an error exception.) - The macros can be defined as expressions checking - * for a variable that is allowed to be changed during execution of a script. - */ +// Configuration whether an exception is thrown on error or interrupt. When +// the preprocessor macros below evaluate to FALSE, an error (did_emsg) or +// interrupt (got_int) under an active try conditional terminates the script +// after the non-active finally clauses of all active try conditionals have been +// executed. Otherwise, errors and/or interrupts are converted into catchable +// exceptions, which terminate the script only if not caught. For user +// exceptions, only current_exception is set. (Note: got_int can be set +// asynchronously afterwards by a SIGINT, so current_exception && got_int is not +// a reliant test that the exception currently being thrown is an interrupt +// exception. Similarly, did_emsg can be set afterwards on an error in an +// (unskipped) conditional command inside an inactive conditional, so +// current_exception && did_emsg is not a reliant test that the exception +// currently being thrown is an error exception.) - The macros can be defined +// as expressions checking for a variable that is allowed to be changed during +// execution of a script. // Values used for the Vim release. #define THROW_ON_ERROR true @@ -68,6 +67,15 @@ #define THROW_ON_INTERRUPT true #define THROW_ON_INTERRUPT_TRUE +// Don't do something after an error, interrupt, or throw, or when +// there is a surrounding conditional and it was not active. +#define CHECK_SKIP \ + (did_emsg \ + || got_int \ + || current_exception \ + || (cstack->cs_idx > 0 \ + && !(cstack->cs_flags[cstack->cs_idx - 1] & CSF_ACTIVE))) + #define discard_pending_return(p) tv_free((typval_T *)(p)) /* @@ -94,7 +102,7 @@ static int cause_abort = FALSE; */ int aborting(void) { - return (did_emsg && force_abort) || got_int || did_throw; + return (did_emsg && force_abort) || got_int || current_exception; } /* @@ -178,8 +186,9 @@ int cause_errthrow(char_u *mesg, int severe, int *ignore) * currently throwing an exception, do nothing. The message text will * then be stored to v:errmsg by emsg() without displaying it. */ - if (((trylevel == 0 && !cause_abort) || emsg_silent) && !did_throw) - return FALSE; + if (((trylevel == 0 && !cause_abort) || emsg_silent) && !current_exception) { + return false; + } /* * Ignore an interrupt message when inside a try conditional or when an @@ -208,12 +217,13 @@ int cause_errthrow(char_u *mesg, int severe, int *ignore) * exception currently being thrown to prevent it from being caught. Just * execute finally clauses and terminate. */ - if (did_throw) { - /* When discarding an interrupt exception, reset got_int to prevent the - * same interrupt being converted to an exception again and discarding - * the error exception we are about to throw here. */ - if (current_exception->type == ET_INTERRUPT) - got_int = FALSE; + if (current_exception) { + // When discarding an interrupt exception, reset got_int to prevent the + // same interrupt being converted to an exception again and discarding + // the error exception we are about to throw here. + if (current_exception->type == ET_INTERRUPT) { + got_int = false; + } discard_current_exception(); } @@ -333,45 +343,44 @@ void do_errthrow(struct condstack *cstack, char_u *cmdname) */ int do_intthrow(struct condstack *cstack) { - /* - * If no interrupt occurred or no try conditional is active and no exception - * is being thrown, do nothing (for compatibility of non-EH scripts). - */ - if (!got_int || (trylevel == 0 && !did_throw)) - return FALSE; + // If no interrupt occurred or no try conditional is active and no exception + // is being thrown, do nothing (for compatibility of non-EH scripts). + if (!got_int || (trylevel == 0 && !current_exception)) { + return false; + } -#ifdef THROW_TEST /* avoid warning for condition always true */ +#ifdef THROW_TEST // avoid warning for condition always true if (!THROW_ON_INTERRUPT) { - /* - * The interrupt aborts everything except for executing finally clauses. - * Discard any user or error or interrupt exception currently being - * thrown. - */ - if (did_throw) + // The interrupt aborts everything except for executing finally clauses. + // Discard any user or error or interrupt exception currently being + // thrown. + if (current_exception) { discard_current_exception(); - } else + } + } else { #endif - { - /* - * Throw an interrupt exception, so that everything will be aborted - * (except for executing finally clauses), until the interrupt exception - * is caught; if still uncaught at the top level, the script processing - * will be terminated then. - If an interrupt exception is already - * being thrown, do nothing. - * - */ - if (did_throw) { - if (current_exception->type == ET_INTERRUPT) - return FALSE; + // Throw an interrupt exception, so that everything will be aborted + // (except for executing finally clauses), until the interrupt exception + // is caught; if still uncaught at the top level, the script processing + // will be terminated then. - If an interrupt exception is already + // being thrown, do nothing. + + if (current_exception) { + if (current_exception->type == ET_INTERRUPT) { + return false; + } - /* An interrupt exception replaces any user or error exception. */ + // An interrupt exception replaces any user or error exception. discard_current_exception(); } - if (throw_exception("Vim:Interrupt", ET_INTERRUPT, NULL) != FAIL) + if (throw_exception("Vim:Interrupt", ET_INTERRUPT, NULL) != FAIL) { do_throw(cstack); + } +#ifdef THROW_TEST } +#endif - return TRUE; + return true; } // Get an exception message that is to be stored in current_exception->value. @@ -565,8 +574,7 @@ void discard_current_exception(void) // Note: all globals manipulated here should be saved/restored in // try_enter/try_leave. current_exception = NULL; - did_throw = FALSE; - need_rethrow = FALSE; + need_rethrow = false; } /* @@ -795,15 +803,7 @@ void ex_if(exarg_T *eap) ++cstack->cs_idx; cstack->cs_flags[cstack->cs_idx] = 0; - /* - * Don't do something after an error, interrupt, or throw, or when there - * is a surrounding conditional and it was not active. - */ - skip = did_emsg || got_int || did_throw || (cstack->cs_idx > 0 - && !(cstack->cs_flags[cstack-> - cs_idx - - 1] & - CSF_ACTIVE)); + skip = CHECK_SKIP; bool error; result = eval_to_bool(eap->arg, &error, &eap->nextcmd, skip); @@ -854,15 +854,7 @@ void ex_else(exarg_T *eap) int result; struct condstack *cstack = eap->cstack; - /* - * Don't do something after an error, interrupt, or throw, or when there is - * a surrounding conditional and it was not active. - */ - skip = did_emsg || got_int || did_throw || (cstack->cs_idx > 0 - && !(cstack->cs_flags[cstack-> - cs_idx - - 1] & - CSF_ACTIVE)); + skip = CHECK_SKIP; if (cstack->cs_idx < 0 || (cstack->cs_flags[cstack->cs_idx] @@ -952,15 +944,7 @@ void ex_while(exarg_T *eap) cstack->cs_flags[cstack->cs_idx] = eap->cmdidx == CMD_while ? CSF_WHILE : CSF_FOR; - /* - * Don't do something after an error, interrupt, or throw, or when - * there is a surrounding conditional and it was not active. - */ - skip = did_emsg || got_int || did_throw || (cstack->cs_idx > 0 - && !(cstack->cs_flags[cstack-> - cs_idx - - 1] & - CSF_ACTIVE)); + skip = CHECK_SKIP; if (eap->cmdidx == CMD_while) { /* * ":while bool-expr" @@ -1233,8 +1217,6 @@ void do_throw(struct condstack *cstack) cstack->cs_flags[idx] &= ~CSF_ACTIVE; cstack->cs_exception[idx] = current_exception; } - - did_throw = TRUE; } /* @@ -1253,15 +1235,7 @@ void ex_try(exarg_T *eap) cstack->cs_flags[cstack->cs_idx] = CSF_TRY; cstack->cs_pending[cstack->cs_idx] = CSTP_NONE; - /* - * Don't do something after an error, interrupt, or throw, or when there - * is a surrounding conditional and it was not active. - */ - skip = did_emsg || got_int || did_throw || (cstack->cs_idx > 0 - && !(cstack->cs_flags[cstack-> - cs_idx - - 1] & - CSF_ACTIVE)); + skip = CHECK_SKIP; if (!skip) { /* Set ACTIVE and TRUE. TRUE means that the corresponding ":catch" @@ -1353,8 +1327,9 @@ void ex_catch(exarg_T *eap) * corresponding try block never got active (because of an inactive * surrounding conditional or after an error or interrupt or throw). */ - if (!did_throw || !(cstack->cs_flags[idx] & CSF_TRUE)) - skip = TRUE; + if (!current_exception || !(cstack->cs_flags[idx] & CSF_TRUE)) { + skip = true; + } /* * Check for a match only if an exception is thrown but not caught by @@ -1413,10 +1388,10 @@ void ex_catch(exarg_T *eap) } if (caught) { - /* Make this ":catch" clause active and reset did_emsg, got_int, - * and did_throw. Put the exception on the caught stack. */ + /* Make this ":catch" clause active and reset did_emsg and got_int. + * Put the exception on the caught stack. */ cstack->cs_flags[idx] |= CSF_ACTIVE | CSF_CAUGHT; - did_emsg = got_int = did_throw = FALSE; + did_emsg = got_int = false; catch_exception((except_T *)cstack->cs_exception[idx]); /* It's mandatory that the current exception is stored in the cstack * so that it can be discarded at the next ":catch", ":finally", or @@ -1426,6 +1401,10 @@ void ex_catch(exarg_T *eap) if (cstack->cs_exception[cstack->cs_idx] != current_exception) { internal_error("ex_catch()"); } + // Discarding current_exceptions happens based on what is stored in + // cstack->cs_exception, *all* calls to discard_current_exception() are + // (and must be) guarded by current_exception check. + current_exception = NULL; } else { /* * If there is a preceding catch clause and it caught the exception, @@ -1484,7 +1463,7 @@ void ex_finally(exarg_T *eap) * interrupt or throw) or for a ":finally" without ":try" or a multiple * ":finally". After every other error (did_emsg or the conditional * errors detected above) or after an interrupt (got_int) or an - * exception (did_throw), the finally clause must be executed. + * exception (current_exception), the finally clause must be executed. */ skip = !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE); @@ -1511,30 +1490,31 @@ void ex_finally(exarg_T *eap) cleanup_conditionals(cstack, CSF_TRY, FALSE); /* - * Make did_emsg, got_int, did_throw pending. If set, they overrule - * a pending ":continue", ":break", ":return", or ":finish". Then - * we have particularly to discard a pending return value (as done + * Make did_emsg, got_int, current_exception pending. If set, they + * overrule a pending ":continue", ":break", ":return", or ":finish". + * Then we have particularly to discard a pending return value (as done * by the call to cleanup_conditionals() above when did_emsg or * got_int is set). The pending values are restored by the * ":endtry", except if there is a new error, interrupt, exception, * ":continue", ":break", ":return", or ":finish" in the following * finally clause. A missing ":endwhile", ":endfor" or ":endif" - * detected here is treated as if did_emsg and did_throw had + * detected here is treated as if did_emsg and current_exception had * already been set, respectively in case that the error is not - * converted to an exception, did_throw had already been unset. + * converted to an exception, current_exception had already been unset. * We must not set did_emsg here since that would suppress the * error message. */ - if (pending == CSTP_ERROR || did_emsg || got_int || did_throw) { + if (pending == CSTP_ERROR || did_emsg || got_int || current_exception) { if (cstack->cs_pending[cstack->cs_idx] == CSTP_RETURN) { report_discard_pending(CSTP_RETURN, cstack->cs_rettv[cstack->cs_idx]); discard_pending_return(cstack->cs_rettv[cstack->cs_idx]); } - if (pending == CSTP_ERROR && !did_emsg) - pending |= (THROW_ON_ERROR) ? CSTP_THROW : 0; - else - pending |= did_throw ? CSTP_THROW : 0; + if (pending == CSTP_ERROR && !did_emsg) { + pending |= (THROW_ON_ERROR ? CSTP_THROW : 0); + } else { + pending |= (current_exception ? CSTP_THROW : 0); + } pending |= did_emsg ? CSTP_ERROR : 0; pending |= got_int ? CSTP_INTERRUPT : 0; assert(pending >= CHAR_MIN && pending <= CHAR_MAX); @@ -1547,14 +1527,15 @@ void ex_finally(exarg_T *eap) * exception. When emsg() is called for a missing ":endif" or * a missing ":endwhile"/":endfor" detected here, the * exception will be discarded. */ - if (did_throw && cstack->cs_exception[cstack->cs_idx] - != current_exception) + if (current_exception + && cstack->cs_exception[cstack->cs_idx] != current_exception) { internal_error("ex_finally()"); + } } /* * Set CSL_HAD_FINA, so do_cmdline() will reset did_emsg, - * got_int, and did_throw and make the finally clause active. + * got_int, and current_exception and make the finally clause active. * This will happen after emsg() has been called for a missing * ":endif" or a missing ":endwhile"/":endfor" detected here, so * that the following finally clause will be executed even then. @@ -1589,7 +1570,7 @@ void ex_endtry(exarg_T *eap) // made inactive by a ":continue", ":break", ":return", or ":finish" in // the finally clause. The latter case need not be tested since then // anything pending has already been discarded. - skip = (did_emsg || got_int || did_throw + skip = (did_emsg || got_int || current_exception || !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE)); if (!(cstack->cs_flags[cstack->cs_idx] & CSF_TRY)) { @@ -1606,12 +1587,13 @@ void ex_endtry(exarg_T *eap) /* * If an exception is being thrown, discard it to prevent it from * being rethrown at the end of this function. It would be - * discarded by the error message, anyway. Resets did_throw. + * discarded by the error message, anyway. Resets current_exception. * This does not affect the script termination due to the error * since "trylevel" is decremented after emsg() has been called. */ - if (did_throw) + if (current_exception) { discard_current_exception(); + } } else { idx = cstack->cs_idx; @@ -1621,9 +1603,11 @@ void ex_endtry(exarg_T *eap) * a finally clause, we need to rethrow it after closing the try * conditional. */ - if (did_throw && (cstack->cs_flags[idx] & CSF_TRUE) - && !(cstack->cs_flags[idx] & CSF_FINALLY)) - rethrow = TRUE; + if (current_exception + && (cstack->cs_flags[idx] & CSF_TRUE) + && !(cstack->cs_flags[idx] & CSF_FINALLY)) { + rethrow = true; + } } /* If there was no finally clause, show the user when debugging or @@ -1644,11 +1628,12 @@ void ex_endtry(exarg_T *eap) if (got_int) { skip = TRUE; (void)do_intthrow(cstack); - /* The do_intthrow() call may have reset did_throw or - * cstack->cs_pending[idx].*/ - rethrow = FALSE; - if (did_throw && !(cstack->cs_flags[idx] & CSF_FINALLY)) - rethrow = TRUE; + // The do_intthrow() call may have reset current_exception or + // cstack->cs_pending[idx]. + rethrow = false; + if (current_exception && !(cstack->cs_flags[idx] & CSF_FINALLY)) { + rethrow = true; + } } } @@ -1710,26 +1695,30 @@ void ex_endtry(exarg_T *eap) do_finish(eap, FALSE); break; - /* When the finally clause was entered due to an error, - * interrupt or throw (as opposed to a ":continue", ":break", - * ":return", or ":finish"), restore the pending values of - * did_emsg, got_int, and did_throw. This is skipped, if there - * was a new error, interrupt, throw, ":continue", ":break", - * ":return", or ":finish". in the finally clause. */ + // When the finally clause was entered due to an error, + // interrupt or throw (as opposed to a ":continue", ":break", + // ":return", or ":finish"), restore the pending values of + // did_emsg, got_int, and current_exception. This is skipped, if there + // was a new error, interrupt, throw, ":continue", ":break", + // ":return", or ":finish". in the finally clause. default: - if (pending & CSTP_ERROR) - did_emsg = TRUE; - if (pending & CSTP_INTERRUPT) - got_int = TRUE; - if (pending & CSTP_THROW) - rethrow = TRUE; + if (pending & CSTP_ERROR) { + did_emsg = true; + } + if (pending & CSTP_INTERRUPT) { + got_int = true; + } + if (pending & CSTP_THROW) { + rethrow = true; + } break; } } - if (rethrow) - /* Rethrow the current exception (within this cstack). */ + if (rethrow) { + // Rethrow the current exception (within this cstack). do_throw(cstack); + } } } @@ -1759,33 +1748,34 @@ void enter_cleanup(cleanup_T *csp) int pending = CSTP_NONE; /* - * Postpone did_emsg, got_int, did_throw. The pending values will be + * Postpone did_emsg, got_int, current_exception. The pending values will be * restored by leave_cleanup() except if there was an aborting error, * interrupt, or uncaught exception after this function ends. */ - if (did_emsg || got_int || did_throw || need_rethrow) { - csp->pending = (did_emsg ? CSTP_ERROR : 0) - | (got_int ? CSTP_INTERRUPT : 0) - | (did_throw ? CSTP_THROW : 0) - | (need_rethrow ? CSTP_THROW : 0); - - /* If we are currently throwing an exception (did_throw), save it as - * well. On an error not yet converted to an exception, update - * "force_abort" and reset "cause_abort" (as do_errthrow() would do). - * This is needed for the do_cmdline() call that is going to be made - * for autocommand execution. We need not save *msg_list because - * there is an extra instance for every call of do_cmdline(), anyway. + if (did_emsg || got_int || current_exception || need_rethrow) { + csp->pending = (did_emsg ? CSTP_ERROR : 0) + | (got_int ? CSTP_INTERRUPT : 0) + | (current_exception ? CSTP_THROW : 0) + | (need_rethrow ? CSTP_THROW : 0); + + /* If we are currently throwing an exception, save it as well. On an error + * not yet converted to an exception, update "force_abort" and reset + * "cause_abort" (as do_errthrow() would do). This is needed for the + * do_cmdline() call that is going to be made for autocommand execution. We + * need not save *msg_list because there is an extra instance for every call + * of do_cmdline(), anyway. */ - if (did_throw || need_rethrow) + if (current_exception || need_rethrow) { csp->exception = current_exception; - else { + } else { csp->exception = NULL; if (did_emsg) { force_abort |= cause_abort; cause_abort = FALSE; } } - did_emsg = got_int = did_throw = need_rethrow = FALSE; + did_emsg = got_int = need_rethrow = false; + current_exception = NULL; /* Report if required by the 'verbose' option or when debugging. */ report_make_pending(pending, csp->exception); @@ -1857,19 +1847,20 @@ void leave_cleanup(cleanup_T *csp) force_abort = FALSE; } - /* - * Restore the pending values of did_emsg, got_int, and did_throw. - */ - if (pending & CSTP_ERROR) - did_emsg = TRUE; - if (pending & CSTP_INTERRUPT) - got_int = TRUE; - if (pending & CSTP_THROW) - need_rethrow = TRUE; /* did_throw will be set by do_one_cmd() */ + // Restore the pending values of did_emsg, got_int, and current_exception. + if (pending & CSTP_ERROR) { + did_emsg = true; + } + if (pending & CSTP_INTERRUPT) { + got_int = true; + } + if (pending & CSTP_THROW) { + need_rethrow = true; // current_exception will be set by do_one_cmd() + } - /* Report if required by the 'verbose' option or when debugging. */ - report_resume_pending(pending, - (pending & CSTP_THROW) ? (void *)current_exception : NULL); + // Report if required by the 'verbose' option or when debugging. + report_resume_pending( + pending, ((pending & CSTP_THROW) ? (void *)current_exception : NULL)); } } diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 52686f6651..4adff63b95 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -1,9 +1,7 @@ // This is an open source non-commercial project. Dear PVS-Studio, please check // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com -/* - * fileio.c: read from and write to a file - */ +// fileio.c: read from and write to a file #include <assert.h> #include <errno.h> @@ -65,57 +63,62 @@ #define BUFSIZE 8192 /* size of normal write buffer */ #define SMBUFSIZE 256 /* size of emergency write buffer */ -/* - * The autocommands are stored in a list for each event. - * Autocommands for the same pattern, that are consecutive, are joined - * together, to avoid having to match the pattern too often. - * The result is an array of Autopat lists, which point to AutoCmd lists: - * - * first_autopat[0] --> Autopat.next --> Autopat.next --> NULL - * Autopat.cmds Autopat.cmds - * | | - * V V - * AutoCmd.next AutoCmd.next - * | | - * V V - * AutoCmd.next NULL - * | - * V - * NULL - * - * first_autopat[1] --> Autopat.next --> NULL - * Autopat.cmds - * | - * V - * AutoCmd.next - * | - * V - * NULL - * etc. - * - * The order of AutoCmds is important, this is the order in which they were - * defined and will have to be executed. - */ +// +// The autocommands are stored in a list for each event. +// Autocommands for the same pattern, that are consecutive, are joined +// together, to avoid having to match the pattern too often. +// The result is an array of Autopat lists, which point to AutoCmd lists: +// +// last_autopat[0] -----------------------------+ +// V +// first_autopat[0] --> Autopat.next --> Autopat.next --> NULL +// Autopat.cmds Autopat.cmds +// | | +// V V +// AutoCmd.next AutoCmd.next +// | | +// V V +// AutoCmd.next NULL +// | +// V +// NULL +// +// last_autopat[1] --------+ +// V +// first_autopat[1] --> Autopat.next --> NULL +// Autopat.cmds +// | +// V +// AutoCmd.next +// | +// V +// NULL +// etc. +// +// The order of AutoCmds is important, this is the order in which they were +// defined and will have to be executed. +// typedef struct AutoCmd { - char_u *cmd; /* The command to be executed (NULL - when command has been removed) */ - char nested; /* If autocommands nest here */ - char last; /* last command in list */ - scid_T scriptID; /* script ID where defined */ - struct AutoCmd *next; /* Next AutoCmd in list */ + char_u *cmd; // The command to be executed (NULL + // when command has been removed) + char nested; // If autocommands nest here + char last; // last command in list + scid_T scriptID; // script ID where defined + struct AutoCmd *next; // Next AutoCmd in list } AutoCmd; typedef struct AutoPat { - char_u *pat; /* pattern as typed (NULL when pattern - has been removed) */ - regprog_T *reg_prog; /* compiled regprog for pattern */ - AutoCmd *cmds; /* list of commands to do */ - struct AutoPat *next; /* next AutoPat in AutoPat list */ - int group; /* group ID */ - int patlen; /* strlen() of pat */ - int buflocal_nr; /* !=0 for buffer-local AutoPat */ - char allow_dirs; /* Pattern may match whole path */ - char last; /* last pattern for apply_autocmds() */ + struct AutoPat *next; // next AutoPat in AutoPat list; MUST + // be the first entry + char_u *pat; // pattern as typed (NULL when pattern + // has been removed) + regprog_T *reg_prog; // compiled regprog for pattern + AutoCmd *cmds; // list of commands to do + int group; // group ID + int patlen; // strlen() of pat + int buflocal_nr; // !=0 for buffer-local AutoPat + char allow_dirs; // Pattern may match whole path + char last; // last pattern for apply_autocmds() } AutoPat; /* @@ -226,6 +229,15 @@ void filemess(buf_T *buf, char_u *name, char_u *s, int attr) msg_scrolled_ign = FALSE; } +static AutoPat *last_autopat[NUM_EVENTS] = { + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL +}; + /* * Read lines from file "fname" into the buffer after line "from". * @@ -1725,9 +1737,17 @@ failed: xfree(buffer); if (read_stdin) { - /* Use stderr for stdin, makes shell commands work. */ close(0); +#ifndef WIN32 + // On Unix, use stderr for stdin, makes shell commands work. ignored = dup(2); +#else + // On Windows, use the console input handle for stdin. + HANDLE conin = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ, (LPSECURITY_ATTRIBUTES)NULL, + OPEN_EXISTING, 0, (HANDLE)NULL); + ignored = _open_osfhandle(conin, _O_RDONLY); +#endif } if (tmpname != NULL) { @@ -4440,7 +4460,7 @@ char *modname(const char *fname, const char *ext, bool prepend_dot) /// @param size size of the buffer /// @param fp file to read from /// -/// @return true for end-of-file. +/// @return true for EOF or error bool vim_fgets(char_u *buf, int size, FILE *fp) FUNC_ATTR_NONNULL_ALL { char *retval; @@ -4451,7 +4471,7 @@ bool vim_fgets(char_u *buf, int size, FILE *fp) FUNC_ATTR_NONNULL_ALL do { errno = 0; retval = fgets((char *)buf, size, fp); - } while (retval == NULL && errno == EINTR); + } while (retval == NULL && errno == EINTR && ferror(fp)); if (buf[size - 2] != NUL && buf[size - 2] != '\n') { char tbuf[200]; @@ -4463,12 +4483,12 @@ bool vim_fgets(char_u *buf, int size, FILE *fp) FUNC_ATTR_NONNULL_ALL tbuf[sizeof(tbuf) - 2] = NUL; errno = 0; retval = fgets((char *)tbuf, sizeof(tbuf), fp); - if (retval == NULL && errno != EINTR) { + if (retval == NULL && (feof(fp) || errno != EINTR)) { break; } } while (tbuf[sizeof(tbuf) - 2] != NUL && tbuf[sizeof(tbuf) - 2] != '\n'); } - return retval ? false : feof(fp); + return retval == NULL; } /// Read 2 bytes from "fd" and turn them into an int, MSB first. @@ -5528,6 +5548,15 @@ static void au_cleanup(void) /* remove the pattern if it has been marked for deletion */ if (ap->pat == NULL) { + if (ap->next == NULL) { + if (prev_ap == &(first_autopat[(int)event])) { + last_autopat[(int)event] = NULL; + } else { + // this depends on the "next" field being the first in + // the struct + last_autopat[(int)event] = (AutoPat *)prev_ap; + } + } *prev_ap = ap->next; vim_regfree(ap->reg_prog); xfree(ap); @@ -6120,10 +6149,13 @@ static int do_autocmd_event(event_T event, char_u *pat, int nested, char_u *cmd, patlen = (int)STRLEN(buflocal_pat); /* but not endpat */ } - /* - * Find AutoPat entries with this pattern. - */ - prev_ap = &first_autopat[(int)event]; + // Find AutoPat entries with this pattern. When adding a command it + // always goes at or after the last one, so start at the end. + if (!forceit && *cmd != NUL && last_autopat[(int)event] != NULL) { + prev_ap = &last_autopat[(int)event]; + } else { + prev_ap = &first_autopat[(int)event]; + } while ((ap = *prev_ap) != NULL) { if (ap->pat != NULL) { /* Accept a pattern when: @@ -6209,6 +6241,7 @@ static int do_autocmd_event(event_T event, char_u *pat, int nested, char_u *cmd, } ap->cmds = NULL; *prev_ap = ap; + last_autopat[(int)event] = ap; ap->next = NULL; if (group == AUGROUP_ALL) ap->group = current_augroup; diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 5d4e61d56a..03929a58b6 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -1577,7 +1577,7 @@ vungetc ( /* unget one character (can only be done once!) */ old_mouse_col = mouse_col; } -/// get a character: +/// Gets a character: /// 1. from the stuffbuffer /// This is used for abbreviated commands like "D" -> "d$". /// Also used to redo a command for ".". @@ -1595,7 +1595,7 @@ vungetc ( /* unget one character (can only be done once!) */ /// if "advance" is FALSE (vpeekc()): /// just look whether there is a character available. /// -/// When "no_mapping" is zero, checks for mappings in the current mode. +/// When `no_mapping` (global) is zero, checks for mappings in the current mode. /// Only returns one byte (of a multi-byte character). /// K_SPECIAL and CSI may be escaped, need to get two more bytes then. static int vgetorpeek(int advance) diff --git a/src/nvim/globals.h b/src/nvim/globals.h index e857f5ff5b..89d93310a6 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -317,17 +317,11 @@ EXTERN int do_profiling INIT(= PROF_NONE); /* PROF_ values */ /* * The exception currently being thrown. Used to pass an exception to * a different cstack. Also used for discarding an exception before it is - * caught or made pending. Only valid when did_throw is TRUE. + * caught or made pending. */ EXTERN except_T *current_exception; /* - * did_throw: An exception is being thrown. Reset when the exception is caught - * or as long as it is pending in a finally clause. - */ -EXTERN int did_throw INIT(= FALSE); - -/* * need_rethrow: set to TRUE when a throw that cannot be handled in do_cmdline() * must be propagated to the cstack of the previously called do_cmdline(). */ @@ -953,6 +947,7 @@ EXTERN int fill_stlnc INIT(= ' '); EXTERN int fill_vert INIT(= 9474); // │ EXTERN int fill_fold INIT(= 183); // · EXTERN int fill_diff INIT(= '-'); +EXTERN int fill_msgsep INIT(= ' '); /* Whether 'keymodel' contains "stopsel" and "startsel". */ EXTERN int km_stopsel INIT(= FALSE); @@ -1075,7 +1070,6 @@ EXTERN char_u e_nesting[] INIT(= N_("E22: Scripts nested too deep")); EXTERN char_u e_noalt[] INIT(= N_("E23: No alternate file")); EXTERN char_u e_noabbr[] INIT(= N_("E24: No such abbreviation")); EXTERN char_u e_nobang[] INIT(= N_("E477: No ! allowed")); -EXTERN char_u e_nogvim[] INIT(= N_("E25: Nvim does not have a built-in GUI")); EXTERN char_u e_nogroup[] INIT(= N_("E28: No such highlight group name: %s")); EXTERN char_u e_noinstext[] INIT(= N_("E29: No inserted text yet")); EXTERN char_u e_nolastcmd[] INIT(= N_("E30: No previous command line")); diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h index 08157935f5..3518c8bdcc 100644 --- a/src/nvim/highlight_defs.h +++ b/src/nvim/highlight_defs.h @@ -87,6 +87,7 @@ typedef enum { , HLF_QFL // selected quickfix line , HLF_0 // Whitespace , HLF_INACTIVE // NormalNC: Normal text in non-current windows + , HLF_MSGSEP // message separator line , HLF_COUNT // MUST be the last one } hlf_T; @@ -137,7 +138,8 @@ EXTERN const char *hlf_names[] INIT(= { [HLF_MC] = "ColorColumn", [HLF_QFL] = "QuickFixLine", [HLF_0] = "Whitespace", - [HLF_INACTIVE] = "NormalNC" + [HLF_INACTIVE] = "NormalNC", + [HLF_MSGSEP] = "MsgSeparator", }); diff --git a/src/nvim/keymap.h b/src/nvim/keymap.h index 00e9cf6ed3..c64691e8ea 100644 --- a/src/nvim/keymap.h +++ b/src/nvim/keymap.h @@ -443,7 +443,7 @@ enum key_extra { #define MOD_MASK_2CLICK 0x20 // use MOD_MASK_MULTI_CLICK #define MOD_MASK_3CLICK 0x40 // use MOD_MASK_MULTI_CLICK #define MOD_MASK_4CLICK 0x60 // use MOD_MASK_MULTI_CLICK -#define MOD_MASK_CMD 0x80 // "super" key (OSX/Mac: command-key) +#define MOD_MASK_CMD 0x80 // "super" key (macOS: command-key) #define MOD_MASK_MULTI_CLICK (MOD_MASK_2CLICK|MOD_MASK_3CLICK| \ MOD_MASK_4CLICK) diff --git a/src/nvim/macros.h b/src/nvim/macros.h index 4e01265498..348df2d9b6 100644 --- a/src/nvim/macros.h +++ b/src/nvim/macros.h @@ -136,13 +136,21 @@ # define RESET_BINDING(wp) (wp)->w_p_scb = FALSE; (wp)->w_p_crb = FALSE -/// Calculate the length of a C array. +/// Calculate the length of a C array /// /// This should be called with a real array. Calling this with a pointer is an -/// error. A mechanism to detect many (though not all) of those errors at compile -/// time is implemented. It works by the second division producing a division by -/// zero in those cases (-Wdiv-by-zero in GCC). -#define ARRAY_SIZE(arr) ((sizeof(arr)/sizeof((arr)[0])) / ((size_t)(!(sizeof(arr) % sizeof((arr)[0]))))) +/// error. A mechanism to detect many (though not all) of those errors at +/// compile time is implemented. It works by the second division producing +/// a division by zero in those cases (-Wdiv-by-zero in GCC). +#define ARRAY_SIZE(arr) \ + ((sizeof(arr)/sizeof((arr)[0])) \ + / ((size_t)(!(sizeof(arr) % sizeof((arr)[0]))))) + +/// Get last array entry +/// +/// This should be called with a real array. Calling this with a pointer is an +/// error. +#define ARRAY_LAST_ENTRY(arr) (arr)[ARRAY_SIZE(arr) - 1] // Duplicated in os/win_defs.h to avoid include-order sensitivity. #define RGB_(r, g, b) ((r << 16) | (g << 8) | b) diff --git a/src/nvim/main.c b/src/nvim/main.c index 4288d7f9d7..ce9feedd16 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -72,30 +72,30 @@ # include "nvim/os/pty_process_unix.h" #endif -/* Maximum number of commands from + or -c arguments. */ +// Maximum number of commands from + or -c arguments. #define MAX_ARG_CMDS 10 -/* values for "window_layout" */ -#define WIN_HOR 1 /* "-o" horizontally split windows */ -#define WIN_VER 2 /* "-O" vertically split windows */ -#define WIN_TABS 3 /* "-p" windows on tab pages */ +// values for "window_layout" +#define WIN_HOR 1 // "-o" horizontally split windows +#define WIN_VER 2 // "-O" vertically split windows +#define WIN_TABS 3 // "-p" windows on tab pages -/* Struct for various parameters passed between main() and other functions. */ +// Struct for various parameters passed between main() and other functions. typedef struct { int argc; char **argv; char *use_vimrc; // vimrc from -u argument - int n_commands; /* no. of commands from + or -c */ + int n_commands; // no. of commands from + or -c char *commands[MAX_ARG_CMDS]; // commands from + or -c arg - char_u cmds_tofree[MAX_ARG_CMDS]; /* commands that need free() */ - int n_pre_commands; /* no. of commands from --cmd */ + char_u cmds_tofree[MAX_ARG_CMDS]; // commands that need free() + int n_pre_commands; // no. of commands from --cmd char *pre_commands[MAX_ARG_CMDS]; // commands from --cmd argument - int edit_type; /* type of editing to do */ - char_u *tagname; /* tag from -t argument */ - char_u *use_ef; /* 'errorfile' from -q argument */ + int edit_type; // type of editing to do + char_u *tagname; // tag from -t argument + char_u *use_ef; // 'errorfile' from -q argument int want_full_screen; bool input_isatty; // stdin is a terminal @@ -103,13 +103,15 @@ typedef struct { bool err_isatty; // stderr is a terminal int no_swap_file; // "-n" argument used int use_debug_break_level; - int window_count; /* number of windows to use */ - int window_layout; /* 0, WIN_HOR, WIN_VER or WIN_TABS */ + int window_count; // number of windows to use + int window_layout; // 0, WIN_HOR, WIN_VER or WIN_TABS #if !defined(UNIX) - int literal; /* don't expand file names */ + int literal; // don't expand file names #endif - int diff_mode; /* start with 'diff' set */ + int diff_mode; // start with 'diff' set + + char *listen_addr; // --listen {address} } mparm_T; /* Values for edit_type. */ @@ -150,7 +152,6 @@ void event_init(void) signal_init(); // finish mspgack-rpc initialization channel_init(); - server_init(); terminal_init(); } @@ -241,9 +242,8 @@ int main(int argc, char **argv) char_u *cwd = NULL; // current workding dir on startup time_init(); - /* Many variables are in "params" so that we can pass them to invoked - * functions without a lot of arguments. "argc" and "argv" are also - * copied, so that they can be changed. */ + // Many variables are in `params` so that we can pass them around easily. + // `argc` and `argv` are also copied, so that they can be changed. init_params(¶ms, argc, argv); init_startuptime(¶ms); @@ -254,11 +254,10 @@ int main(int argc, char **argv) check_and_set_isatty(¶ms); event_init(); - /* - * Process the command line arguments. File names are put in the global - * argument list "global_alist". - */ + // Process the command line arguments. File names are put in the global + // argument list "global_alist". command_line_scan(¶ms); + server_init(params.listen_addr); if (GARGCOUNT > 0) { fname = get_fname(¶ms, cwd); @@ -819,6 +818,9 @@ static void command_line_scan(mparm_T *parmp) if (!channel_from_stdio(true, CALLBACK_READER_INIT, &err)) { abort(); } + } else if (STRNICMP(argv[0] + argv_idx, "listen", 6) == 0) { + want_argument = true; + argv_idx += 6; } else if (STRNICMP(argv[0] + argv_idx, "literal", 7) == 0) { #if !defined(UNIX) parmp->literal = TRUE; @@ -864,10 +866,6 @@ static void command_line_scan(mparm_T *parmp) case 'f': /* "-f" GUI: run in foreground. */ break; - case 'g': /* "-g" start GUI */ - main_start_gui(); - break; - case 'F': { // "-F" start in Farsi mode: rl + fkmap set. p_fkmap = true; set_option_value("rl", 1L, NULL, 0); @@ -906,18 +904,8 @@ static void command_line_scan(mparm_T *parmp) parmp->no_swap_file = TRUE; break; - case 'p': /* "-p[N]" open N tab pages */ -#ifdef TARGET_API_MAC_OSX - /* For some reason on MacOS X, an argument like: - -psn_0_10223617 is passed in when invoke from Finder - or with the 'open' command */ - if (argv[0][argv_idx] == 's') { - argv_idx = -1; /* bypass full -psn */ - main_start_gui(); - break; - } -#endif - /* default is 0: open window for each file */ + case 'p': // "-p[N]" open N tab pages + // default is 0: open window for each file parmp->window_count = get_number_arg(argv[0], &argv_idx, 0); parmp->window_layout = WIN_TABS; break; @@ -1030,15 +1018,12 @@ static void command_line_scan(mparm_T *parmp) mainerr(err_opt_unknown, argv[0]); } - /* - * Handle option arguments with argument. - */ + // Handle option arguments with argument. if (want_argument) { - /* - * Check for garbage immediately after the option letter. - */ - if (argv[0][argv_idx] != NUL) + // Check for garbage immediately after the option letter. + if (argv[0][argv_idx] != NUL) { mainerr(err_opt_garbage, argv[0]); + } --argc; if (argc < 1 && c != 'S') /* -S has an optional argument */ @@ -1077,13 +1062,17 @@ static void command_line_scan(mparm_T *parmp) break; case '-': - if (argv[-1][2] == 'c') { - /* "--cmd {command}" execute command */ - if (parmp->n_pre_commands >= MAX_ARG_CMDS) + if (strequal(argv[-1], "--cmd")) { + // "--cmd {command}" execute command + if (parmp->n_pre_commands >= MAX_ARG_CMDS) { mainerr(err_extra_cmd, NULL); + } parmp->pre_commands[parmp->n_pre_commands++] = argv[0]; + } else if (strequal(argv[-1], "--listen")) { + // "--listen {address}" + parmp->listen_addr = argv[0]; } - /* "--startuptime <file>" already handled */ + // "--startuptime <file>" already handled break; case 'q': /* "-q {errorfile}" QuickFix mode */ @@ -1224,11 +1213,10 @@ static void init_params(mparm_T *paramp, int argc, char **argv) paramp->want_full_screen = true; paramp->use_debug_break_level = -1; paramp->window_count = -1; + paramp->listen_addr = NULL; } -/* - * Initialize global startuptime file if "--startuptime" passed as an argument. - */ +/// Initialize global startuptime file if "--startuptime" passed as an argument. static void init_startuptime(mparm_T *paramp) { for (int i = 1; i < paramp->argc; i++) { @@ -1834,17 +1822,6 @@ static void source_startup_scripts(const mparm_T *const parmp) TIME_MSG("sourcing vimrc file(s)"); } -/* - * Setup to start using the GUI. Exit with an error when not available. - */ -static void main_start_gui(void) -{ - mch_errmsg(_(e_nogvim)); - mch_errmsg("\n"); - mch_exit(2); -} - - /// Get an environment variable, and execute it as Ex commands. /// /// @param env environment variable to execute @@ -1968,6 +1945,7 @@ static void usage(void) mch_msg(_(" --api-info Write msgpack-encoded API metadata to stdout\n")); mch_msg(_(" --embed Use stdin/stdout as a msgpack-rpc channel\n")); mch_msg(_(" --headless Don't start a user interface\n")); + mch_msg(_(" --listen <address> Start RPC server at this address\n")); #if !defined(UNIX) mch_msg(_(" --literal Don't expand wildcards\n")); #endif diff --git a/src/nvim/map.h b/src/nvim/map.h index 047aa163ce..ac1239a548 100644 --- a/src/nvim/map.h +++ b/src/nvim/map.h @@ -8,6 +8,11 @@ #include "nvim/api/private/dispatch.h" #include "nvim/bufhl_defs.h" +#if defined(__NetBSD__) +# undef uint64_t +# define uint64_t uint64_t +#endif + #define MAP_DECLS(T, U) \ KHASH_DECLARE(T##_##U##_map, T, U) \ \ diff --git a/src/nvim/message.c b/src/nvim/message.c index 12e5b844be..04528629c7 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -1875,13 +1875,29 @@ bool message_filtered(char_u *msg) return cmdmod.filter_force ? match : !match; } +/// including horizontal separator +int msg_scrollsize(void) +{ + return msg_scrolled + p_ch + 1; +} + /* * Scroll the screen up one line for displaying the next message line. */ static void msg_scroll_up(void) { - /* scrolling up always works */ - screen_del_lines(0, 0, 1, (int)Rows, NULL); + if (dy_flags & DY_MSGSEP) { + if (msg_scrolled == 0) { + screen_fill(Rows-p_ch-1, Rows-p_ch, 0, (int)Columns, + fill_msgsep, fill_msgsep, hl_attr(HLF_MSGSEP)); + } + int nscroll = MIN(msg_scrollsize()+1, Rows); + ui_call_set_scroll_region(Rows-nscroll, Rows-1, 0, Columns-1); + screen_del_lines(Rows-nscroll, 0, 1, nscroll, NULL); + ui_reset_scroll_region(); + } else { + screen_del_lines(0, 0, 1, (int)Rows, NULL); + } } /* diff --git a/src/nvim/msgpack_rpc/server.c b/src/nvim/msgpack_rpc/server.c index 9bf122f4db..e5d80aea1d 100644 --- a/src/nvim/msgpack_rpc/server.c +++ b/src/nvim/msgpack_rpc/server.c @@ -32,26 +32,27 @@ static garray_T watchers = GA_EMPTY_INIT_VALUE; #endif /// Initializes the module -bool server_init(void) +bool server_init(const char *listen_addr) { ga_init(&watchers, sizeof(SocketWatcher *), 1); - bool must_free = false; - const char *listen_address = os_getenv(LISTEN_ADDRESS_ENV_VAR); - if (listen_address == NULL) { - must_free = true; - listen_address = server_address_new(); - } + // $NVIM_LISTEN_ADDRESS + const char *env_addr = os_getenv(LISTEN_ADDRESS_ENV_VAR); + int rv = listen_addr == NULL ? 1 : server_start(listen_addr); - if (!listen_address) { - return false; + if (0 != rv) { + rv = env_addr == NULL ? 1 : server_start(env_addr); + if (0 != rv) { + listen_addr = server_address_new(); + if (listen_addr == NULL) { + return false; + } + rv = server_start(listen_addr); + xfree((char *)listen_addr); + } } - bool ok = (server_start(listen_address) == 0); - if (must_free) { - xfree((char *) listen_address); - } - return ok; + return rv == 0; } /// Teardown a single server @@ -120,8 +121,8 @@ bool server_owns_pipe_address(const char *path) /// @param endpoint Address of the server. Either a 'ip:[port]' string or an /// arbitrary identifier (trimmed to 256 bytes) for the Unix /// socket or named pipe. -/// @returns 0 on success, 1 on a regular error, and negative errno -/// on failure to bind or listen. +/// @returns 0: success, 1: validation error, 2: already listening, +/// -errno: failed to bind or listen. int server_start(const char *endpoint) { if (endpoint == NULL || endpoint[0] == '\0') { @@ -145,7 +146,7 @@ int server_start(const char *endpoint) uv_freeaddrinfo(watcher->uv.tcp.addrinfo); } socket_watcher_close(watcher, free_server); - return 1; + return 2; } } @@ -177,7 +178,7 @@ int server_start(const char *endpoint) /// Stops listening on the address specified by `endpoint`. /// /// @param endpoint Address of the server. -void server_stop(char *endpoint) +bool server_stop(char *endpoint) { SocketWatcher *watcher; bool watcher_found = false; @@ -196,8 +197,8 @@ void server_stop(char *endpoint) } if (!watcher_found) { - ELOG("Not listening on %s", addr); - return; + WLOG("Not listening on %s", addr); + return false; } // Unset $NVIM_LISTEN_ADDRESS if it is the stopped address. @@ -219,6 +220,8 @@ void server_stop(char *endpoint) if (STRCMP(addr, get_vim_var_str(VV_SEND_SERVER)) == 0) { set_vservername(&watchers); } + + return true; } /// Returns an allocated array of server addresses. diff --git a/src/nvim/option.c b/src/nvim/option.c index d6903c8db7..c43ba2fc4f 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -13,7 +13,8 @@ // add some code to didset_window_options(). // - For a buffer option, add some code to buf_copy_options(). // - For a buffer string option, add code to check_buf_options(). -// - If it's a numeric option, add any necessary bounds checks to do_set(). +// - If it's a numeric option, add any necessary bounds checks to +// set_num_option(). // - If it's a list of flags, add some code in do_set(), search for WW_ALL. // - When adding an option with expansion (P_EXPAND), but with a different // default for Vi and Vim (no P_VI_DEF), add some code at VIMEXP. @@ -1467,8 +1468,7 @@ do_set ( goto skip; } } else if (*arg == '-' || ascii_isdigit(*arg)) { - // Allow negative (for 'undolevels'), octal and - // hex numbers. + // Allow negative, octal and hex numbers. vim_str2nr(arg, NULL, &i, STR2NR_ALL, &value, NULL, 0); if (arg[i] != NUL && !ascii_iswhite(arg[i])) { errmsg = e_invarg; @@ -3411,6 +3411,7 @@ static char_u *set_chars_option(char_u **varp) { &fill_vert, "vert" , 9474 }, // │ { &fill_fold, "fold" , 183 }, // · { &fill_diff, "diff" , '-' }, + { &fill_msgsep, "msgsep", ' ' }, }; static struct charstab lcstab[] = { { &lcs_eol, "eol", NUL }, @@ -4089,238 +4090,259 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, return (char *)e_secure; } - *pp = value; - /* Remember where the option was set. */ - set_option_scriptID_idx(opt_idx, opt_flags, current_SID); - - if (curbuf->b_p_sw < 0) { - errmsg = e_positive; - curbuf->b_p_sw = curbuf->b_p_ts; + // Many number options assume their value is in the signed int range. + if (value < INT_MIN || value > INT_MAX) { + return (char *)e_invarg; } - /* - * Number options that need some action when changed - */ - if (pp == &p_wh || pp == &p_hh) { - if (p_wh < 1) { + // Options that need some validation. + if (pp == &p_wh) { + if (value < 1) { errmsg = e_positive; - p_wh = 1; - } - if (p_wmh > p_wh) { + } else if (p_wmh > value) { errmsg = e_winheight; - p_wh = p_wmh; } - if (p_hh < 0) { + } else if (pp == &p_hh) { + if (value < 0) { errmsg = e_positive; - p_hh = 0; } - - /* Change window height NOW */ - if (!ONE_WINDOW) { - if (pp == &p_wh && curwin->w_height < p_wh) - win_setheight((int)p_wh); - if (pp == &p_hh && curbuf->b_help && curwin->w_height < p_hh) - win_setheight((int)p_hh); - } - } - /* 'winminheight' */ - else if (pp == &p_wmh) { - if (p_wmh < 0) { + } else if (pp == &p_wmh) { + if (value < 0) { errmsg = e_positive; - p_wmh = 0; - } - if (p_wmh > p_wh) { + } else if (value > p_wh) { errmsg = e_winheight; - p_wmh = p_wh; } - win_setminheight(); } else if (pp == &p_wiw) { - if (p_wiw < 1) { + if (value < 1) { errmsg = e_positive; - p_wiw = 1; + } else if (p_wmw > value) { + errmsg = e_winwidth; } - if (p_wmw > p_wiw) { + } else if (pp == &p_wmw) { + if (value < 0) { + errmsg = e_positive; + } else if (value > p_wiw) { errmsg = e_winwidth; - p_wiw = p_wmw; } - - /* Change window width NOW */ - if (!ONE_WINDOW && curwin->w_width < p_wiw) - win_setwidth((int)p_wiw); - } - /* 'winminwidth' */ - else if (pp == &p_wmw) { - if (p_wmw < 0) { + } else if (pp == &p_mco) { + if (value > MAX_MCO) { + errmsg = e_invarg; + } else if (value < 0) { errmsg = e_positive; - p_wmw = 0; } - if (p_wmw > p_wiw) { - errmsg = e_winwidth; - p_wmw = p_wiw; + } else if (pp == &p_titlelen) { + if (value < 0) { + errmsg = e_positive; } - win_setminheight(); - } else if (pp == &p_ls) { - /* (re)set last window status line */ - last_status(false); - } - /* (re)set tab page line */ - else if (pp == &p_stal) { - shell_new_rows(); /* recompute window positions and heights */ - } - /* 'foldlevel' */ - else if (pp == &curwin->w_p_fdl) { - if (curwin->w_p_fdl < 0) - curwin->w_p_fdl = 0; - newFoldLevel(); - } - /* 'foldminlines' */ - else if (pp == &curwin->w_p_fml) { - foldUpdateAll(curwin); - } - /* 'foldnestmax' */ - else if (pp == &curwin->w_p_fdn) { - if (foldmethodIsSyntax(curwin) || foldmethodIsIndent(curwin)) - foldUpdateAll(curwin); - } - /* 'foldcolumn' */ - else if (pp == &curwin->w_p_fdc) { - if (curwin->w_p_fdc < 0) { + } else if (pp == &p_uc) { + if (value < 0) { errmsg = e_positive; - curwin->w_p_fdc = 0; - } else if (curwin->w_p_fdc > 12) { - errmsg = e_invarg; - curwin->w_p_fdc = 12; } - // 'shiftwidth' or 'tabstop' - } else if (pp == &curbuf->b_p_sw || pp == (long *)&curbuf->b_p_ts) { - if (foldmethodIsIndent(curwin)) { - foldUpdateAll(curwin); + } else if (pp == &p_ch) { + if (value < 1) { + errmsg = e_positive; } - // When 'shiftwidth' changes, or it's zero and 'tabstop' changes: - // parse 'cinoptions'. - if (pp == &curbuf->b_p_sw || curbuf->b_p_sw == 0) { - parse_cino(curbuf); + } else if (pp == &p_tm) { + if (value < 0) { + errmsg = e_positive; } - } - /* 'maxcombine' */ - else if (pp == &p_mco) { - if (p_mco > MAX_MCO) - p_mco = MAX_MCO; - else if (p_mco < 0) - p_mco = 0; - screenclear(); /* will re-allocate the screen */ - } else if (pp == &curbuf->b_p_iminsert) { - if (curbuf->b_p_iminsert < 0 || curbuf->b_p_iminsert > B_IMODE_LAST) { + } else if (pp == &p_hi) { + if (value < 0) { + errmsg = e_positive; + } else if (value > 10000) { errmsg = e_invarg; - curbuf->b_p_iminsert = B_IMODE_NONE; } - p_iminsert = curbuf->b_p_iminsert; - showmode(); - /* Show/unshow value of 'keymap' in status lines. */ - status_redraw_curbuf(); - } else if (pp == &p_window) { - if (p_window < 1) - p_window = 1; - else if (p_window >= Rows) - p_window = Rows - 1; - } else if (pp == &curbuf->b_p_imsearch) { - if (curbuf->b_p_imsearch < -1 || curbuf->b_p_imsearch > B_IMODE_LAST) { + } else if (pp == &p_re) { + if (value < 0 || value > 2) { errmsg = e_invarg; - curbuf->b_p_imsearch = B_IMODE_NONE; } - p_imsearch = curbuf->b_p_imsearch; - } else if (pp == &p_channel || pp == &curbuf->b_p_channel) { - errmsg = e_invarg; - *pp = old_value; - } - /* if 'titlelen' has changed, redraw the title */ - else if (pp == &p_titlelen) { - if (p_titlelen < 0) { + } else if (pp == &p_report) { + if (value < 0) { errmsg = e_positive; - p_titlelen = 85; } - if (starting != NO_SCREEN && old_value != p_titlelen) - need_maketitle = TRUE; - } - /* if p_ch changed value, change the command line height */ - else if (pp == &p_ch) { - if (p_ch < 1) { + } else if (pp == &p_titlelen) { + if (value < 0) { errmsg = e_positive; - p_ch = 1; } - if (p_ch > Rows - min_rows() + 1) - p_ch = Rows - min_rows() + 1; - - /* Only compute the new window layout when startup has been - * completed. Otherwise the frame sizes may be wrong. */ - if (p_ch != old_value && full_screen - ) - command_height(); - } - /* when 'updatecount' changes from zero to non-zero, open swap files */ - else if (pp == &p_uc) { - if (p_uc < 0) { + } else if (pp == &p_so) { + if (value < 0 && full_screen) { + errmsg = e_scroll; + } + } else if (pp == &p_siso) { + if (value < 0 && full_screen) { errmsg = e_positive; - p_uc = 100; } - if (p_uc && !old_value) - ml_open_files(); - } else if (pp == &curwin->w_p_cole) { - if (curwin->w_p_cole < 0) { + } else if (pp == &p_cwh) { + if (value < 1) { + errmsg = e_positive; + } + } else if (pp == &p_ut) { + if (value < 0) { + errmsg = e_positive; + } + } else if (pp == &p_ss) { + if (value < 0) { + errmsg = e_positive; + } + } else if (pp == &curwin->w_p_fdl + || pp == (long *)GLOBAL_WO(&curwin->w_p_fdl)) { + if (value < 0) { + errmsg = e_positive; + } + } else if (pp == &curwin->w_p_fdc + || pp == (long *)GLOBAL_WO(&curwin->w_p_fdc)) { + if (value < 0) { errmsg = e_positive; - curwin->w_p_cole = 0; - } else if (curwin->w_p_cole > 3) { + } else if (value > 12) { errmsg = e_invarg; - curwin->w_p_cole = 3; - } - } - /* sync undo before 'undolevels' changes */ - else if (pp == &p_ul) { - /* use the old value, otherwise u_sync() may not work properly */ - p_ul = old_value; - u_sync(TRUE); - p_ul = value; - } else if (pp == &curbuf->b_p_ul) { - /* use the old value, otherwise u_sync() may not work properly */ - curbuf->b_p_ul = old_value; - u_sync(TRUE); - curbuf->b_p_ul = value; - } - /* 'numberwidth' must be positive */ - else if (pp == &curwin->w_p_nuw) { - if (curwin->w_p_nuw < 1) { + } + } else if (pp == &curwin->w_p_cole + || pp == (long *)GLOBAL_WO(&curwin->w_p_cole)) { + if (value < 0) { errmsg = e_positive; - curwin->w_p_nuw = 1; + } else if (value > 3) { + errmsg = e_invarg; } - if (curwin->w_p_nuw > 10) { + } else if (pp == &curwin->w_p_nuw + || pp == (long *)GLOBAL_WO(&curwin->w_p_nuw)) { + if (value < 1) { + errmsg = e_positive; + } else if (value > 10) { errmsg = e_invarg; - curwin->w_p_nuw = 10; } - curwin->w_nrwidth_line_count = 0; - } else if (pp == &curbuf->b_p_tw) { - if (curbuf->b_p_tw < 0) { + } else if (pp == &curbuf->b_p_iminsert || pp == &p_iminsert) { + if (value < 0 || value > B_IMODE_LAST) { + errmsg = e_invarg; + } + } else if (pp == &curbuf->b_p_imsearch || pp == &p_imsearch) { + if (value < -1 || value > B_IMODE_LAST) { + errmsg = e_invarg; + } + } else if (pp == &curbuf->b_p_channel || pp == &p_channel) { + errmsg = e_invarg; + } else if (pp == &curbuf->b_p_scbk || pp == &p_scbk) { + if (value < -1 || value > SB_MAX + || (value != -1 && opt_flags == OPT_LOCAL && !curbuf->terminal)) { + errmsg = e_invarg; + } + } else if (pp == &curbuf->b_p_sw || pp == &p_sw) { + if (value < 0) { + errmsg = e_positive; + } + } else if (pp == &curbuf->b_p_ts || pp == &p_ts) { + if (value < 1) { + errmsg = e_positive; + } + } else if (pp == &curbuf->b_p_tw || pp == &p_tw) { + if (value < 0) { errmsg = e_positive; - curbuf->b_p_tw = 0; } + } + // Don't change the value and return early if validation failed. + if (errmsg != NULL) { + return (char *)errmsg; + } + + *pp = value; + // Remember where the option was set. + set_option_scriptID_idx(opt_idx, opt_flags, current_SID); + + // For these options we want to fix some invalid values. + if (pp == &p_window) { + if (p_window < 1) { + p_window = Rows - 1; + } else if (p_window >= Rows) { + p_window = Rows - 1; + } + } else if (pp == &p_ch) { + if (p_ch > Rows - min_rows() + 1) { + p_ch = Rows - min_rows() + 1; + } + } + + // Number options that need some action when changed + if (pp == &p_wh) { + if (!ONE_WINDOW && curwin->w_height < p_wh) { + win_setheight((int)p_wh); + } + } else if (pp == &p_hh) { + if (!ONE_WINDOW && curbuf->b_help && curwin->w_height < p_hh) { + win_setheight((int)p_hh); + } + } else if (pp == &p_wmh) { + win_setminheight(); + } else if (pp == &p_wiw) { + if (!ONE_WINDOW && curwin->w_width < p_wiw) { + win_setwidth((int)p_wiw); + } + } else if (pp == &p_ls) { + last_status(false); // (re)set last window status line. + } else if (pp == &p_stal) { + // (re)set tab page line + shell_new_rows(); // recompute window positions and heights + } else if (pp == &curwin->w_p_fdl) { + newFoldLevel(); + } else if (pp == &curwin->w_p_fml) { + foldUpdateAll(curwin); + } else if (pp == &curwin->w_p_fdn) { + if (foldmethodIsSyntax(curwin) || foldmethodIsIndent(curwin)) { + foldUpdateAll(curwin); + } + } else if (pp == &curbuf->b_p_sw || pp == &curbuf->b_p_ts) { + // 'shiftwidth' or 'tabstop' + if (foldmethodIsIndent(curwin)) { + foldUpdateAll(curwin); + } + // When 'shiftwidth' changes, or it's zero and 'tabstop' changes: + // parse 'cinoptions'. + if (pp == &curbuf->b_p_sw || curbuf->b_p_sw == 0) { + parse_cino(curbuf); + } + } else if (pp == &p_mco) { + screenclear(); // will re-allocate the screen + } else if (pp == &curbuf->b_p_iminsert) { + showmode(); + // Show/unshow value of 'keymap' in status lines. + status_redraw_curbuf(); + } else if (pp == &p_titlelen) { + // if 'titlelen' has changed, redraw the title + if (starting != NO_SCREEN && old_value != p_titlelen) { + need_maketitle = true; + } + } else if (pp == &p_ch) { + // if p_ch changed value, change the command line height + // Only compute the new window layout when startup has been + // completed. Otherwise the frame sizes may be wrong. + if (p_ch != old_value && full_screen) { + command_height(); + } + } else if (pp == &p_uc) { + // when 'updatecount' changes from zero to non-zero, open swap files + if (p_uc && !old_value) { + ml_open_files(); + } + } else if (pp == &p_ul || pp == &curbuf->b_p_ul) { + // sync undo before 'undolevels' changes + // use the old value, otherwise u_sync() may not work properly + *pp = old_value; + u_sync(true); + *pp = value; + } else if (pp == &curbuf->b_p_tw) { FOR_ALL_TAB_WINDOWS(tp, wp) { check_colorcolumn(wp); } } else if (pp == &curbuf->b_p_scbk || pp == &p_scbk) { - // 'scrollback' - if (*pp < -1 || *pp > SB_MAX - || (*pp != -1 && opt_flags == OPT_LOCAL && !curbuf->terminal)) { - errmsg = e_invarg; - *pp = old_value; - } else if (curbuf->terminal) { + if (curbuf->terminal) { // Force the scrollback to take effect. terminal_resize(curbuf->terminal, UINT16_MAX, UINT16_MAX); } + } else if (pp == &curwin->w_p_nuw) { + curwin->w_nrwidth_line_count = 0; } - /* - * Check the bounds for numeric options here - */ + + // Check the (new) bounds for Rows and Columns here. if (Rows < min_rows() && full_screen) { if (errbuf != NULL) { vim_snprintf((char *)errbuf, errbuflen, @@ -4340,19 +4362,17 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, limit_screen_size(); - /* - * If the screen (shell) height has been changed, assume it is the - * physical screenheight. - */ + // If the screen (shell) height has been changed, assume it is the + // physical screenheight. if (old_Rows != Rows || old_Columns != Columns) { - /* Changing the screen size is not allowed while updating the screen. */ + // Changing the screen size is not allowed while updating the screen. if (updating_screen) { *pp = old_value; } else if (full_screen) { screen_resize((int)Columns, (int)Rows); } else { - /* Postpone the resizing; check the size and cmdline position for - * messages. */ + // Postpone the resizing; check the size and cmdline position for + // messages. check_shellsize(); if (cmdline_row > Rows - p_ch && Rows > p_ch) { assert(p_ch >= 0 && Rows - p_ch <= INT_MAX); @@ -4364,14 +4384,6 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, } } - if (curbuf->b_p_ts <= 0) { - errmsg = e_positive; - curbuf->b_p_ts = 8; - } - if (p_tm < 0) { - errmsg = e_positive; - p_tm = 0; - } if ((curwin->w_p_scr <= 0 || (curwin->w_p_scr > curwin->w_height && curwin->w_height > 0)) @@ -4388,21 +4400,6 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, else /* curwin->w_p_scr > curwin->w_height */ curwin->w_p_scr = curwin->w_height; } - if (p_hi < 0) { - errmsg = e_positive; - p_hi = 0; - } else if (p_hi > 10000) { - errmsg = e_invarg; - p_hi = 10000; - } - if (p_re < 0 || p_re > 2) { - errmsg = e_invarg; - p_re = 0; - } - if (p_report < 0) { - errmsg = e_positive; - p_report = 1; - } if ((p_sj < -100 || p_sj >= Rows) && full_screen) { if (Rows != old_Rows) /* Rows changed, just adjust p_sj */ p_sj = Rows / 2; @@ -4411,30 +4408,11 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, p_sj = 1; } } - if (p_so < 0 && full_screen) { - errmsg = e_scroll; - p_so = 0; - } - if (p_siso < 0 && full_screen) { - errmsg = e_positive; - p_siso = 0; - } - if (p_cwh < 1) { - errmsg = e_positive; - p_cwh = 1; - } - if (p_ut < 0) { - errmsg = e_positive; - p_ut = 2000; - } - if (p_ss < 0) { - errmsg = e_positive; - p_ss = 0; - } - /* May set global value for local option. */ - if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) + // May set global value for local option. + if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) { *(long *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL) = *pp; + } if (pp == &curbuf->b_p_scbk && !curbuf->terminal) { // Normal buffer: reset local 'scrollback' after updating the global value. diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index e2e98f251e..66a49fd6e0 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -394,11 +394,13 @@ EXTERN char_u *p_dir; /* 'directory' */ EXTERN char_u *p_dy; /* 'display' */ EXTERN unsigned dy_flags; #ifdef IN_OPTION_C -static char *(p_dy_values[]) = { "lastline", "truncate", "uhex", NULL }; +static char *(p_dy_values[]) = { "lastline", "truncate", "uhex", "msgsep", + NULL }; #endif #define DY_LASTLINE 0x001 #define DY_TRUNCATE 0x002 #define DY_UHEX 0x004 +#define DY_MSGSEP 0x008 EXTERN int p_ed; // 'edcompatible' EXTERN int p_emoji; // 'emoji' EXTERN char_u *p_ead; // 'eadirection' diff --git a/src/nvim/options.lua b/src/nvim/options.lua index d653147943..80484d0ad2 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -642,7 +642,7 @@ return { vim=true, redraw={'all_windows'}, varname='p_dy', - defaults={if_true={vi="", vim="lastline"}} + defaults={if_true={vi="", vim="lastline,msgsep"}} }, { full_name='eadirection', abbreviation='ead', diff --git a/src/nvim/os/process.c b/src/nvim/os/process.c index e23ba8a4ee..a67e7487eb 100644 --- a/src/nvim/os/process.c +++ b/src/nvim/os/process.c @@ -12,12 +12,16 @@ # include <tlhelp32.h> // for CreateToolhelp32Snapshot #endif -#if defined(__FreeBSD__) // XXX: OpenBSD, NetBSD ? +#if defined(__FreeBSD__) // XXX: OpenBSD ? # include <string.h> # include <sys/types.h> # include <sys/user.h> #endif +#if defined(__NetBSD__) || defined(__OpenBSD__) +# include <sys/param.h> +#endif + #if defined(__APPLE__) || defined(BSD) # include <sys/sysctl.h> # include <pwd.h> @@ -155,7 +159,13 @@ int os_proc_children(int ppid, int **proc_list, size_t *proc_count) # define KP_PID(o) o.p_pid # define KP_PPID(o) o.p_ppid # endif +# ifdef __NetBSD__ + static int name[] = { + CTL_KERN, KERN_PROC2, KERN_PROC_ALL, 0, (int)(sizeof(struct kinfo_proc2)), 0 + }; +# else static int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 }; +# endif // Get total process count. size_t len = 0; @@ -165,7 +175,11 @@ int os_proc_children(int ppid, int **proc_list, size_t *proc_count) } // Get ALL processes. +# ifdef __NetBSD__ + struct kinfo_proc2 *p_list = xmalloc(len); +# else struct kinfo_proc *p_list = xmalloc(len); +# endif rv = sysctl(name, ARRAY_SIZE(name) - 1, p_list, &len, NULL, 0); if (rv) { xfree(p_list); diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c index f650a51fe7..04f59d7522 100644 --- a/src/nvim/os/shell.c +++ b/src/nvim/os/shell.c @@ -136,7 +136,7 @@ int os_call_shell(char_u *cmd, ShellOpts opts, char_u *extra_args) xfree(input.data); if (output) { - (void)write_output(output, nread, true, true); + (void)write_output(output, nread, true); xfree(output); } @@ -388,10 +388,10 @@ static bool out_data_decide_throttle(size_t size) pulse_msg[1] = (tick == 0 || 1 == tick) ? ' ' : '.'; pulse_msg[2] = (tick == 0 || 1 == tick || 2 == tick) ? ' ' : '.'; if (visit == 1) { - screen_del_lines(0, 0, 1, (int)Rows, NULL); + msg_putchar('\n'); } - int lastrow = (int)Rows - 1; - screen_puts_len((char_u *)pulse_msg, ARRAY_SIZE(pulse_msg), lastrow, 0, 0); + msg_putchar('\r'); // put cursor at start of line + msg_puts(pulse_msg); ui_flush(); return true; } @@ -609,28 +609,20 @@ static void read_input(DynamicBuffer *buf) } } -static size_t write_output(char *output, size_t remaining, bool to_buffer, - bool eof) +static size_t write_output(char *output, size_t remaining, bool eof) { if (!output) { return 0; } - char replacement_NUL = to_buffer ? NL : 1; char *start = output; size_t off = 0; - int lastrow = (int)Rows - 1; while (off < remaining) { if (output[off] == NL) { // Insert the line - if (to_buffer) { - output[off] = NUL; - ml_append(curwin->w_cursor.lnum++, (char_u *)output, (int)off + 1, - false); - } else { - screen_del_lines(0, 0, 1, (int)Rows, NULL); - screen_puts_len((char_u *)output, (int)off, lastrow, 0, 0); - } + output[off] = NUL; + ml_append(curwin->w_cursor.lnum++, (char_u *)output, (int)off + 1, + false); size_t skip = off + 1; output += skip; remaining -= skip; @@ -640,24 +632,19 @@ static size_t write_output(char *output, size_t remaining, bool to_buffer, if (output[off] == NUL) { // Translate NUL to NL - output[off] = replacement_NUL; + output[off] = NL; } off++; } if (eof) { if (remaining) { - if (to_buffer) { - // append unfinished line - ml_append(curwin->w_cursor.lnum++, (char_u *)output, 0, false); - // remember that the NL was missing - curbuf->b_no_eol_lnum = curwin->w_cursor.lnum; - } else { - screen_del_lines(0, 0, 1, (int)Rows, NULL); - screen_puts_len((char_u *)output, (int)remaining, lastrow, 0, 0); - } + // append unfinished line + ml_append(curwin->w_cursor.lnum++, (char_u *)output, 0, false); + // remember that the NL was missing + curbuf->b_no_eol_lnum = curwin->w_cursor.lnum; output += remaining; - } else if (to_buffer) { + } else { curbuf->b_no_eol_lnum = 0; } } diff --git a/src/nvim/os/stdpaths.c b/src/nvim/os/stdpaths.c index a41fb7c621..866a005228 100644 --- a/src/nvim/os/stdpaths.c +++ b/src/nvim/os/stdpaths.c @@ -88,7 +88,7 @@ char *stdpaths_get_xdg_var(const XDGVarType idx) /// /// In WIN32 get_xdg_home(kXDGDataHome) returns `{xdg_directory}/nvim-data` to /// avoid storing configuration and data files in the same path. -static char *get_xdg_home(const XDGVarType idx) +char *get_xdg_home(const XDGVarType idx) FUNC_ATTR_WARN_UNUSED_RESULT { char *dir = stdpaths_get_xdg_var(idx); diff --git a/src/nvim/po/CMakeLists.txt b/src/nvim/po/CMakeLists.txt index 121f22129a..94cc63baea 100644 --- a/src/nvim/po/CMakeLists.txt +++ b/src/nvim/po/CMakeLists.txt @@ -2,10 +2,8 @@ find_package(Gettext) find_program(XGETTEXT_PRG xgettext) find_program(ICONV_PRG iconv) -if(HAVE_WORKING_LIBINTL AND GETTEXT_FOUND AND XGETTEXT_PRG AND ICONV_PRG) - set(ENV{OLD_PO_FILE_INPUT} yes) - set(ENV{OLD_PO_FILE_OUTPUT} yes) - +option(LANGUAGES "Localizations to build") +if(NOT LANGUAGES) set(LANGUAGES af ca @@ -31,6 +29,12 @@ if(HAVE_WORKING_LIBINTL AND GETTEXT_FOUND AND XGETTEXT_PRG AND ICONV_PRG) vi zh_CN.UTF-8 zh_TW.UTF-8) +endif() + +if(HAVE_WORKING_LIBINTL AND GETTEXT_FOUND AND XGETTEXT_PRG AND ICONV_PRG) + set(ENV{OLD_PO_FILE_INPUT} yes) + set(ENV{OLD_PO_FILE_OUTPUT} yes) + set(NVIM_RELATIVE_SOURCES) foreach(SRC ${NVIM_SOURCES} ${NVIM_HEADERS}) @@ -135,22 +139,30 @@ if(HAVE_WORKING_LIBINTL AND GETTEXT_FOUND AND XGETTEXT_PRG AND ICONV_PRG) endmacro() # Create some translations from others. - BuildPoIconv(ja utf-8 euc-jp) - BuildMo(ja.euc-jp) + if(";${LANGUAGES};" MATCHES ";ja;") + BuildPoIconv(ja utf-8 euc-jp) + BuildMo(ja.euc-jp) + endif() - BuildPoIconv(cs ISO-8859-2 cp1250) - BuildMo(cs.cp1250) + if(";${LANGUAGES};" MATCHES ";cs;") + BuildPoIconv(cs ISO-8859-2 cp1250) + BuildMo(cs.cp1250) + endif() - BuildPoIconv(sk ISO-8859-2 cp1250) - BuildMo(sk.cp1250) + if(";${LANGUAGES};" MATCHES ";sk;") + BuildPoIconv(sk ISO-8859-2 cp1250) + BuildMo(sk.cp1250) + endif() add_custom_target(update-po-nb COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/no.po ${CMAKE_CURRENT_SOURCE_DIR}/nb.po DEPENDS no.po) list(APPEND UPDATE_PO_TARGETS update-po-nb) - CheckPo(nb) - BuildMo(nb) + if(";${LANGUAGES};" MATCHES ";no;") + CheckPo(nb) + BuildMo(nb) + endif() foreach(LANGUAGE ${LANGUAGES}) set(poFile "${CMAKE_CURRENT_SOURCE_DIR}/${LANGUAGE}.po") diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 2d8c353f92..46124e0672 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -3029,7 +3029,7 @@ static void qf_fill_buffer(qf_info_T *qi, buf_T *buf, qfline_T *old_last) /* * Return TRUE if "buf" is the quickfix buffer. */ -int bt_quickfix(buf_T *buf) +int bt_quickfix(const buf_T *const buf) { return buf != NULL && buf->b_p_bt[0] == 'q'; } diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 22de08041a..731bb2658a 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -300,13 +300,25 @@ void update_screen(int type) * if the screen was scrolled up when displaying a message, scroll it down */ if (msg_scrolled) { - clear_cmdline = TRUE; - if (msg_scrolled > Rows - 5) /* clearing is faster */ + clear_cmdline = true; + if (dy_flags & DY_MSGSEP) { + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { + int valid = Rows - msg_scrollsize(); + if (wp->w_winrow + wp->w_height > valid) { + wp->w_redr_type = NOT_VALID; + wp->w_lines_valid = 0; + } + if (wp->w_winrow + wp->w_height + wp->w_status_height > valid) { + wp->w_redr_status = true; + } + } + } else if (msg_scrolled > Rows - 5) { // clearing is faster type = CLEAR; - else if (type != CLEAR) { - check_for_delay(FALSE); - if (screen_ins_lines(0, 0, msg_scrolled, (int)Rows, NULL) == FAIL) + } else if (type != CLEAR) { + check_for_delay(false); + if (screen_ins_lines(0, 0, msg_scrolled, (int)Rows, NULL) == FAIL) { type = CLEAR; + } FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp->w_winrow < msg_scrolled) { if (wp->w_winrow + wp->w_height > msg_scrolled @@ -2032,7 +2044,7 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T } screen_line(row + wp->w_winrow, wp->w_wincol, wp->w_width, - wp->w_width, false, wp); + wp->w_width, false, wp, 0); /* * Update w_cline_height and w_cline_folded if the cursor line was @@ -2453,10 +2465,6 @@ win_line ( line_attr = win_hl_attr(wp, HLF_QFL); } - if (wp->w_hl_attr_normal != 0) { - line_attr = hl_combine_attr(wp->w_hl_attr_normal, line_attr); - } - if (line_attr != 0) { area_highlighting = true; } @@ -2914,7 +2922,8 @@ win_line ( && lnum == wp->w_cursor.lnum && vcol >= (long)wp->w_virtcol && filler_todo <= 0 ) { - screen_line(screen_row, wp->w_wincol, col, -wp->w_width, wp->w_p_rl, wp); + screen_line(screen_row, wp->w_wincol, col, -wp->w_width, wp->w_p_rl, wp, + wp->w_hl_attr_normal); // Pretend we have finished updating the window. Except when // 'cursorcolumn' is set. if (wp->w_p_cuc) { @@ -4007,7 +4016,8 @@ win_line ( col++; } } - screen_line(screen_row, wp->w_wincol, col, wp->w_width, wp->w_p_rl, wp); + screen_line(screen_row, wp->w_wincol, col, wp->w_width, wp->w_p_rl, wp, + wp->w_hl_attr_normal); row++; /* @@ -4230,7 +4240,7 @@ win_line ( || (n_extra != 0 && (c_extra != NUL || *p_extra != NUL))) ) { screen_line(screen_row, wp->w_wincol, col - boguscols, - wp->w_width, wp->w_p_rl, wp); + wp->w_width, wp->w_p_rl, wp, wp->w_hl_attr_normal); boguscols = 0; ++row; ++screen_row; @@ -4400,7 +4410,7 @@ static int char_needs_redraw(int off_from, int off_to, int cols) * When FALSE and "clear_width" > 0, clear columns "endcol" to "clear_width" */ static void screen_line(int row, int coloff, int endcol, - int clear_width, int rlflag, win_T *wp) + int clear_width, int rlflag, win_T *wp, int bg_attr) { unsigned off_from; unsigned off_to; @@ -4433,15 +4443,16 @@ static void screen_line(int row, int coloff, int endcol, /* Clear rest first, because it's left of the text. */ if (clear_width > 0) { while (col <= endcol && ScreenLines[off_to] == ' ' - && ScreenAttrs[off_to] == 0 + && ScreenAttrs[off_to] == bg_attr && (!enc_utf8 || ScreenLinesUC[off_to] == 0) ) { ++off_to; ++col; } - if (col <= endcol) - screen_fill(row, row + 1, col + coloff, - endcol + coloff + 1, ' ', ' ', 0); + if (col <= endcol) { + screen_fill(row, row + 1, col + coloff, endcol + coloff + 1, ' ', ' ', + bg_attr); + } } col = endcol + 1; off_to = LineOffset[row] + col + coloff; @@ -4449,6 +4460,13 @@ static void screen_line(int row, int coloff, int endcol, endcol = (clear_width > 0 ? clear_width : -clear_width); } + if (bg_attr) { + for (int c = col; c < endcol; c++) { + ScreenAttrs[off_from+c] = hl_combine_attr(bg_attr, + ScreenAttrs[off_from+c]); + } + } + redraw_next = char_needs_redraw(off_from, off_to, endcol - col); while (col < endcol) { @@ -4547,15 +4565,15 @@ static void screen_line(int row, int coloff, int endcol, /* blank out the rest of the line */ while (col < clear_width && ScreenLines[off_to] == ' ' - && ScreenAttrs[off_to] == 0 + && ScreenAttrs[off_to] == bg_attr && (!enc_utf8 || ScreenLinesUC[off_to] == 0) ) { ++off_to; ++col; } if (col < clear_width) { - screen_fill(row, row + 1, col + coloff, clear_width + coloff, - ' ', ' ', 0); + screen_fill(row, row + 1, col + coloff, clear_width + coloff, ' ', ' ', + bg_attr); off_to += clear_width - col; col = clear_width; } diff --git a/src/nvim/shada.c b/src/nvim/shada.c index 605d9c30a6..15ac28e1bf 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -16,12 +16,14 @@ #include "nvim/os/os.h" #include "nvim/os/time.h" #include "nvim/vim.h" +#include "nvim/pos.h" #include "nvim/ascii.h" #include "nvim/shada.h" #include "nvim/message.h" #include "nvim/globals.h" #include "nvim/memory.h" #include "nvim/mark.h" +#include "nvim/macros.h" #include "nvim/ops.h" #include "nvim/garray.h" #include "nvim/option.h" @@ -375,7 +377,8 @@ KHASH_MAP_INIT_STR(file_marks, FileMarks) /// Before actually writing most of the data is read to this structure. typedef struct { HistoryMergerState hms[HIST_COUNT]; ///< Structures for history merging. - PossiblyFreedShadaEntry global_marks[NGLOBALMARKS]; ///< All global marks. + PossiblyFreedShadaEntry global_marks[NMARKS]; ///< Named global marks. + PossiblyFreedShadaEntry numbered_marks[EXTRA_MARKS]; ///< Numbered marks. PossiblyFreedShadaEntry registers[NUM_SAVED_REGISTERS]; ///< All registers. PossiblyFreedShadaEntry jumps[JUMPLISTSIZE]; ///< All dumped jumps. size_t jumps_size; ///< Number of jumps occupied. @@ -2020,6 +2023,113 @@ shada_parse_msgpack_extra_bytes: return ret; } +/// Format shada entry for debugging purposes +/// +/// @param[in] entry ShaDa entry to format. +/// +/// @return string representing ShaDa entry in a static buffer. +static const char *shada_format_entry(const ShadaEntry entry) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_UNUSED FUNC_ATTR_NONNULL_RET +{ + static char ret[1024]; + ret[0] = 0; + vim_snprintf(S_LEN(ret), "[ ] ts=%" PRIu64 " "); + // ^ Space for `can_free_entry` + switch (entry.type) { + case kSDItemMissing: { + vim_snprintf_add(S_LEN(ret), "Missing"); + break; + } + case kSDItemHeader: { + vim_snprintf_add(S_LEN(ret), "Header { TODO }"); + break; + } + case kSDItemBufferList: { + vim_snprintf_add(S_LEN(ret), "BufferList { TODO }"); + break; + } + case kSDItemUnknown: { + vim_snprintf_add(S_LEN(ret), "Unknown { TODO }"); + break; + } + case kSDItemSearchPattern: { + vim_snprintf_add(S_LEN(ret), "SearchPattern { TODO }"); + break; + } + case kSDItemSubString: { + vim_snprintf_add(S_LEN(ret), "SubString { TODO }"); + break; + } + case kSDItemHistoryEntry: { + vim_snprintf_add(S_LEN(ret), "HistoryEntry { TODO }"); + break; + } + case kSDItemRegister: { + vim_snprintf_add(S_LEN(ret), "Register { TODO }"); + break; + } + case kSDItemVariable: { + vim_snprintf_add(S_LEN(ret), "Variable { TODO }"); + break; + } +#define FORMAT_MARK_ENTRY(entry_name, name_fmt, name_fmt_arg) \ + do { \ + typval_T ad_tv = { \ + .v_type = VAR_DICT, \ + .vval.v_dict = entry.data.filemark.additional_data \ + }; \ + size_t ad_len; \ + char *const ad = encode_tv2string(&ad_tv, &ad_len); \ + vim_snprintf_add( \ + S_LEN(ret), \ + entry_name " {" name_fmt " file=[%zu]\"%.512s\", " \ + "pos={l=%" PRIdLINENR ",c=%" PRIdCOLNR ",a=%" PRIdCOLNR "}, " \ + "ad={%p:[%zu]%.64s} }", \ + name_fmt_arg, \ + strlen(entry.data.filemark.fname), \ + entry.data.filemark.fname, \ + entry.data.filemark.mark.lnum, \ + entry.data.filemark.mark.col, \ + entry.data.filemark.mark.coladd, \ + entry.data.filemark.additional_data, \ + ad_len, \ + ad); \ + } while (0) + case kSDItemGlobalMark: { + FORMAT_MARK_ENTRY("GlobalMark", " name='%c',", entry.data.filemark.name); + break; + } + case kSDItemChange: { + FORMAT_MARK_ENTRY("Change", "%s", ""); + break; + } + case kSDItemLocalMark: { + FORMAT_MARK_ENTRY("LocalMark", " name='%c',", entry.data.filemark.name); + break; + } + case kSDItemJump: { + FORMAT_MARK_ENTRY("Jump", "%s", ""); + break; + } +#undef FORMAT_MARK_ENTRY + } + return ret; +} + +/// Format possibly freed shada entry for debugging purposes +/// +/// @param[in] entry ShaDa entry to format. +/// +/// @return string representing ShaDa entry in a static buffer. +static const char *shada_format_pfreed_entry( + const PossiblyFreedShadaEntry pfs_entry) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_UNUSED FUNC_ATTR_NONNULL_RET +{ + char *ret = (char *)shada_format_entry(pfs_entry.data); + ret[1] = (pfs_entry.can_free_entry ? 'T' : 'F'); + return ret; +} + /// Read and merge in ShaDa file, used when writing /// /// @param[in] sd_reader Structure containing file reader definition. @@ -2071,9 +2181,12 @@ static inline ShaDaWriteResult shada_read_when_writing( shada_free_shada_entry(&wms_entry->data); \ } \ } \ - wms_entry->can_free_entry = true; \ - wms_entry->data = (entry); \ + *wms_entry = pfs_entry; \ } while (0) + const PossiblyFreedShadaEntry pfs_entry = { + .can_free_entry = true, + .data = entry, + }; switch (entry.type) { case kSDItemMissing: { break; @@ -2125,13 +2238,49 @@ static inline ShaDaWriteResult shada_read_when_writing( break; } case kSDItemGlobalMark: { - const int idx = mark_global_index(entry.data.filemark.name); - if (idx < 0) { - ret = shada_pack_entry(packer, entry, 0); - shada_free_shada_entry(&entry); - break; + if (ascii_isdigit(entry.data.filemark.name)) { + bool processed_mark = false; + // Completely ignore numbered mark names, make a list sorted by + // timestamp. + for (size_t i = ARRAY_SIZE(wms->numbered_marks); i > 0; i--) { + ShadaEntry wms_entry = wms->numbered_marks[i - 1].data; + if (wms_entry.type != kSDItemGlobalMark) { + continue; + } + // Ignore duplicates. + if (wms_entry.timestamp == entry.timestamp + && (wms_entry.data.filemark.additional_data == NULL + && entry.data.filemark.additional_data == NULL) + && marks_equal(wms_entry.data.filemark.mark, + entry.data.filemark.mark) + && strcmp(wms_entry.data.filemark.fname, + entry.data.filemark.fname) == 0) { + shada_free_shada_entry(&entry); + processed_mark = true; + break; + } + if (wms_entry.timestamp >= entry.timestamp) { + processed_mark = true; + if (i < ARRAY_SIZE(wms->numbered_marks)) { + replace_numbered_mark(wms, i, pfs_entry); + } else { + shada_free_shada_entry(&entry); + } + break; + } + } + if (!processed_mark) { + replace_numbered_mark(wms, 0, pfs_entry); + } + } else { + const int idx = mark_global_index(entry.data.filemark.name); + if (idx < 0) { + ret = shada_pack_entry(packer, entry, 0); + shada_free_shada_entry(&entry); + break; + } + COMPARE_WITH_ENTRY(&wms->global_marks[idx], entry); } - COMPARE_WITH_ENTRY(&wms->global_marks[idx], entry); break; } case kSDItemChange: @@ -2175,8 +2324,7 @@ static inline ShaDaWriteResult shada_read_when_writing( shada_free_shada_entry(&wms_entry->data); } } - wms_entry->can_free_entry = true; - wms_entry->data = entry; + *wms_entry = pfs_entry; } } else { #define FREE_POSSIBLY_FREED_SHADA_ENTRY(entry) \ @@ -2216,6 +2364,20 @@ static inline ShaDaWriteResult shada_read_when_writing( return ret; } +/// Check whether buffer should be ignored +/// +/// @param[in] buf buf_T* to check. +/// @param[in] removable_bufs Cache of buffers ignored due to their location. +/// +/// @return true or false. +static inline bool ignore_buf(const buf_T *const buf, + khash_t(bufset) *const removable_bufs) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE +{ + return (buf->b_ffname == NULL || !buf->b_p_bl || bt_quickfix(buf) \ + || in_bufset(removable_bufs, buf)); +} + /// Get list of buffers to write to the shada file /// /// @param[in] removable_bufs Buffers which are ignored @@ -2227,11 +2389,9 @@ static inline ShadaEntry shada_get_buflist( { int max_bufs = get_shada_parameter('%'); size_t buf_count = 0; -#define IGNORE_BUF(buf)\ - (buf->b_ffname == NULL || !buf->b_p_bl || bt_quickfix(buf) \ - || in_bufset(removable_bufs, buf)) // NOLINT(whitespace/indent) FOR_ALL_BUFFERS(buf) { - if (!IGNORE_BUF(buf) && (max_bufs < 0 || buf_count < (size_t)max_bufs)) { + if (!ignore_buf(buf, removable_bufs) + && (max_bufs < 0 || buf_count < (size_t)max_bufs)) { buf_count++; } } @@ -2249,7 +2409,7 @@ static inline ShadaEntry shada_get_buflist( }; size_t i = 0; FOR_ALL_BUFFERS(buf) { - if (IGNORE_BUF(buf)) { + if (ignore_buf(buf, removable_bufs)) { continue; } if (i >= buf_count) { @@ -2263,7 +2423,6 @@ static inline ShadaEntry shada_get_buflist( i++; } -#undef IGNORE_BUF return buflist_entry; } @@ -2365,6 +2524,34 @@ static inline void shada_initialize_registers(WriteMergerState *const wms, } while (reg_iter != NULL); } +/// Replace numbered mark in WriteMergerState +/// +/// Frees the last mark, moves (including adjusting mark names) marks from idx +/// to the last-but-one one and saves the new mark at given index. +/// +/// @param[out] wms Merger state to adjust. +/// @param[in] idx Index at which new mark should be placed. +/// @param[in] entry New mark. +static inline void replace_numbered_mark(WriteMergerState *const wms, + const size_t idx, + const PossiblyFreedShadaEntry entry) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_ALWAYS_INLINE +{ + if (ARRAY_LAST_ENTRY(wms->numbered_marks).can_free_entry) { + shada_free_shada_entry(&ARRAY_LAST_ENTRY(wms->numbered_marks).data); + } + for (size_t i = idx; i < ARRAY_SIZE(wms->numbered_marks) - 1; i++) { + if (wms->numbered_marks[i].data.type == kSDItemGlobalMark) { + wms->numbered_marks[i].data.data.filemark.name = (char)('0' + (int)i + 1); + } + } + memmove(wms->numbered_marks + idx + 1, wms->numbered_marks + idx, + sizeof(wms->numbered_marks[0]) + * (ARRAY_SIZE(wms->numbered_marks) - 1 - idx)); + wms->numbered_marks[idx] = entry; + wms->numbered_marks[idx].data.data.filemark.name = (char)('0' + (int)idx); +} + /// Write ShaDa file /// /// @param[in] sd_writer Structure containing file writer definition. @@ -2597,6 +2784,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, // Initialize global marks if (dump_global_marks) { const void *global_mark_iter = NULL; + size_t digit_mark_idx = 0; do { char name = NUL; xfmark_T fm; @@ -2619,7 +2807,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, } fname = (const char *) buf->b_ffname; } - wms->global_marks[mark_global_index(name)] = (PossiblyFreedShadaEntry) { + const PossiblyFreedShadaEntry pf_entry = { .can_free_entry = false, .data = { .type = kSDItemGlobalMark, @@ -2629,11 +2817,16 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, .mark = fm.fmark.mark, .name = name, .additional_data = fm.fmark.additional_data, - .fname = (char *) fname, + .fname = (char *)fname, } } }, }; + if (ascii_isdigit(name)) { + replace_numbered_mark(wms, digit_mark_idx++, pf_entry); + } else { + wms->global_marks[mark_global_index(name)] = pf_entry; + } } while (global_mark_iter != NULL); } @@ -2715,6 +2908,26 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, } } + // Update numbered marks: '0' should be replaced with the current position, + // '9' should be removed and all other marks shifted. + if (!ignore_buf(curbuf, &removable_bufs) && curwin->w_cursor.lnum != 0) { + replace_numbered_mark(wms, 0, (PossiblyFreedShadaEntry) { + .can_free_entry = false, + .data = { + .type = kSDItemGlobalMark, + .timestamp = os_time(), + .data = { + .filemark = { + .mark = curwin->w_cursor, + .name = '0', + .additional_data = NULL, + .fname = (char *)curbuf->b_ffname, + } + } + }, + }); + } + // Write the rest #define PACK_WMS_ARRAY(wms_array) \ do { \ @@ -2729,6 +2942,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, } \ } while (0) PACK_WMS_ARRAY(wms->global_marks); + PACK_WMS_ARRAY(wms->numbered_marks); PACK_WMS_ARRAY(wms->registers); for (size_t i = 0; i < wms->jumps_size; i++) { if (shada_pack_pfreed_entry(packer, wms->jumps[i], max_kbyte) @@ -2823,6 +3037,7 @@ shada_write_exit: return ret; } +#undef IGNORE_BUF #undef PACK_STATIC_STR /// Write ShaDa file to a given location diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 8ec393e568..2613c09c19 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -5999,6 +5999,7 @@ static const char *highlight_init_both[] = { "default link QuickFixLine Search", "default link Substitute Search", "default link Whitespace NonText", + "default link MsgSeparator StatusLine", NULL }; diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index a31e1843fc..4bfcbf8e79 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -20,13 +20,13 @@ SCRIPTS_DEFAULT = \ test40.out \ test42.out \ test48.out \ - test49.out \ test52.out \ test64.out \ ifneq ($(OS),Windows_NT) SCRIPTS_DEFAULTS := $(SCRIPTS_DEFAULT) \ test17.out \ + test49.out \ endif diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim index 6a7b44a76f..5c98455909 100644 --- a/src/nvim/testdir/runtest.vim +++ b/src/nvim/testdir/runtest.vim @@ -61,7 +61,7 @@ set nomore lang mess C " Always use forward slashes. -set shellslash +" set shellslash " Prepare for calling test_garbagecollect_now(). let v:testing = 1 @@ -237,6 +237,7 @@ let s:flaky = [ \ 'Test_oneshot()', \ 'Test_out_cb()', \ 'Test_paused()', + \ 'Test_quoteplus()', \ 'Test_reltime()', \ 'Test_terminal_composing_unicode()', \ 'Test_terminal_redir_file()', diff --git a/src/nvim/testdir/setup.vim b/src/nvim/testdir/setup.vim index 7d6dd0c7ce..aac9fefef4 100644 --- a/src/nvim/testdir/setup.vim +++ b/src/nvim/testdir/setup.vim @@ -17,3 +17,11 @@ let &packpath = &rtp " Make sure $HOME does not get read or written. let $HOME = '/does/not/exist' + +" Use default shell on Windows to avoid segfault, caused by TUI +if has('win32') + let $SHELL = '' + let $TERM = '' + let &shell = empty($COMSPEC) ? exepath('cmd.exe') : $COMSPEC + set shellcmdflag=/s/c shellxquote=\" shellredir=>%s\ 2>&1 +endif diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim index 673246e1fb..be68e9ff9d 100644 --- a/src/nvim/testdir/test_cmdline.vim +++ b/src/nvim/testdir/test_cmdline.vim @@ -154,7 +154,7 @@ func Test_getcompletion() call assert_equal([], l) let l = getcompletion('', 'dir') - call assert_true(index(l, 'sautest/') >= 0) + call assert_true(index(l, expand('sautest/')) >= 0) let l = getcompletion('NoMatch', 'dir') call assert_equal([], l) @@ -246,7 +246,7 @@ func Test_getcompletion() " Command line completion tests let l = getcompletion('cd ', 'cmdline') - call assert_true(index(l, 'sautest/') >= 0) + call assert_true(index(l, expand('sautest/')) >= 0) let l = getcompletion('cd NoMatch', 'cmdline') call assert_equal([], l) let l = getcompletion('let v:n', 'cmdline') @@ -288,7 +288,7 @@ func Test_expand_star_star() call mkdir('a/b', 'p') call writefile(['asdfasdf'], 'a/b/fileXname') call feedkeys(":find **/fileXname\<Tab>\<CR>", 'xt') - call assert_equal('find a/b/fileXname', getreg(':')) + call assert_equal('find '.expand('a/b/fileXname'), getreg(':')) bwipe! call delete('a', 'rf') endfunc diff --git a/src/nvim/testdir/test_find_complete.vim b/src/nvim/testdir/test_find_complete.vim index 4732109ed0..1019246404 100644 --- a/src/nvim/testdir/test_find_complete.vim +++ b/src/nvim/testdir/test_find_complete.vim @@ -3,6 +3,8 @@ " Do all the tests in a separate window to avoid E211 when we recursively " delete the Xfind directory during cleanup func Test_find_complete() + let shellslash = &shellslash + set shellslash set belloff=all " On windows a stale "Xfind" directory may exist, remove it so that @@ -154,4 +156,5 @@ func Test_find_complete() exe 'cd ' . cwd call delete('Xfind', 'rf') set path& + let &shellslash = shellslash endfunc diff --git a/src/nvim/testdir/test_help_tagjump.vim b/src/nvim/testdir/test_help_tagjump.vim index 06c48d8e76..4d4a902031 100644 --- a/src/nvim/testdir/test_help_tagjump.vim +++ b/src/nvim/testdir/test_help_tagjump.vim @@ -30,7 +30,7 @@ func Test_help_tagjump() help sp?it call assert_equal("help", &filetype) - call assert_true(getline('.') =~ '\*:split\*') + call assert_true(getline('.') =~ '\*'.(has('win32') ? 'split()' : ':split').'\*') helpclose help :? diff --git a/src/nvim/testdir/test_makeencoding.vim b/src/nvim/testdir/test_makeencoding.vim index a3d5538a47..6e4c7af821 100644 --- a/src/nvim/testdir/test_makeencoding.vim +++ b/src/nvim/testdir/test_makeencoding.vim @@ -13,12 +13,19 @@ endif let s:script = 'test_makeencoding.py' -let s:message_tbl = { +if has('iconv') + let s:message_tbl = { \ 'utf-8': 'ÀÈÌÒÙ こんにちは 你好', \ 'latin1': 'ÀÈÌÒÙ', \ 'cp932': 'こんにちは', \ 'cp936': '你好', \} +else + let s:message_tbl = { + \ 'utf-8': 'ÀÈÌÒÙ こんにちは 你好', + \ 'latin1': 'ÀÈÌÒÙ', + \} +endif " Tests for :cgetfile and :lgetfile. diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim index a15d15213a..f8c3161b40 100644 --- a/src/nvim/testdir/test_options.vim +++ b/src/nvim/testdir/test_options.vim @@ -158,6 +158,8 @@ func Test_set_completion() call assert_equal('"set fileencodings:ucs-bom,utf-8,default,latin1', @:) " Expand directories. + let shellslash = &shellslash + set shellslash call feedkeys(":set cdpath=./\<C-A>\<C-B>\"\<CR>", 'tx') call assert_match('./samples/ ', @:) call assert_notmatch('./small.vim ', @:) @@ -168,6 +170,7 @@ func Test_set_completion() call feedkeys(":set tags=./\\\\ dif\<C-A>\<C-B>\"\<CR>", 'tx') call assert_equal('"set tags=./\\ diff diffexpr diffopt', @:) + let &shellslash = shellslash endfunc func Test_set_errors() diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index dd177fd633..85f93cf3da 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -850,17 +850,17 @@ func s:dir_stack_tests(cchar) let qf = g:Xgetlist() - call assert_equal('dir1/a/habits2.txt', bufname(qf[1].bufnr)) + call assert_equal(expand('dir1/a/habits2.txt'), bufname(qf[1].bufnr)) call assert_equal(1, qf[1].lnum) - call assert_equal('dir1/a/b/habits3.txt', bufname(qf[3].bufnr)) + call assert_equal(expand('dir1/a/b/habits3.txt'), bufname(qf[3].bufnr)) call assert_equal(2, qf[3].lnum) - call assert_equal('dir1/a/habits2.txt', bufname(qf[4].bufnr)) + call assert_equal(expand('dir1/a/habits2.txt'), bufname(qf[4].bufnr)) call assert_equal(7, qf[4].lnum) - call assert_equal('dir1/c/habits4.txt', bufname(qf[6].bufnr)) + call assert_equal(expand('dir1/c/habits4.txt'), bufname(qf[6].bufnr)) call assert_equal(3, qf[6].lnum) call assert_equal('habits1.txt', bufname(qf[9].bufnr)) call assert_equal(4, qf[9].lnum) - call assert_equal('dir2/habits5.txt', bufname(qf[11].bufnr)) + call assert_equal(expand('dir2/habits5.txt'), bufname(qf[11].bufnr)) call assert_equal(5, qf[11].lnum) let &efm=save_efm @@ -1065,7 +1065,7 @@ func Test_efm2() call assert_equal(8, len(l)) call assert_equal(89, l[4].lnum) call assert_equal(1, l[4].valid) - call assert_equal('unittests/dbfacadeTest.py', bufname(l[4].bufnr)) + call assert_equal(expand('unittests/dbfacadeTest.py'), bufname(l[4].bufnr)) " The following sequence of commands used to crash Vim set efm=%W%m @@ -1609,11 +1609,11 @@ func Test_two_windows() laddexpr 'one.txt:3:one one one' let loc_one = getloclist(one_id) - call assert_equal('Xone/a/one.txt', bufname(loc_one[1].bufnr)) + call assert_equal(expand('Xone/a/one.txt'), bufname(loc_one[1].bufnr)) call assert_equal(3, loc_one[1].lnum) let loc_two = getloclist(two_id) - call assert_equal('Xtwo/a/two.txt', bufname(loc_two[1].bufnr)) + call assert_equal(expand('Xtwo/a/two.txt'), bufname(loc_two[1].bufnr)) call assert_equal(5, loc_two[1].lnum) call win_gotoid(one_id) diff --git a/src/nvim/testdir/test_recover.vim b/src/nvim/testdir/test_recover.vim index 46d884a97c..beecb4cd0d 100644 --- a/src/nvim/testdir/test_recover.vim +++ b/src/nvim/testdir/test_recover.vim @@ -6,11 +6,6 @@ func Test_recover_root_dir() set dir=/ call assert_fails('recover', 'E305:') close! - - if has('win32') || filewritable('/') == 2 - " can write in / directory on MS-Windows - set dir=/notexist/ - endif call assert_fails('split Xtest', 'E303:') set dir& endfunc diff --git a/src/nvim/testdir/test_stat.vim b/src/nvim/testdir/test_stat.vim index 1239fe9427..0a09130b0c 100644 --- a/src/nvim/testdir/test_stat.vim +++ b/src/nvim/testdir/test_stat.vim @@ -86,7 +86,7 @@ func Test_win32_symlink_dir() let res = system('dir C:\Users /a') if match(res, '\C<SYMLINKD> *All Users') >= 0 " Get the filetype of the symlink. - call assert_equal('dir', getftype('C:\Users\All Users')) + call assert_equal('link', getftype('C:\Users\All Users')) endif endif endfunc diff --git a/src/nvim/testdir/test_system.vim b/src/nvim/testdir/test_system.vim index ce9d110d82..d3c0594c03 100644 --- a/src/nvim/testdir/test_system.vim +++ b/src/nvim/testdir/test_system.vim @@ -5,14 +5,12 @@ function! Test_System() return endif let out = system('echo 123') - " On Windows we may get a trailing space. - if out != "123 \n" - call assert_equal("123\n", out) - endif + call assert_equal("123\n", out) let out = systemlist('echo 123') - " On Windows we may get a trailing space and CR. - if out != ["123 \r"] + if &shell =~# 'cmd.exe$' + call assert_equal(["123\r"], out) + else call assert_equal(['123'], out) endif diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim index d377062780..81ac2b6171 100644 --- a/src/nvim/testdir/test_timers.vim +++ b/src/nvim/testdir/test_timers.vim @@ -119,7 +119,7 @@ func Test_paused() let slept = WaitFor('g:val == 1') call assert_equal(1, g:val) if has('reltime') - call assert_inrange(0, 60, slept) + call assert_inrange(0, 100, slept) else call assert_inrange(0, 10, slept) endif diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index bfebe0442d..72a25b0b59 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -450,7 +450,8 @@ static void update_attrs(UI *ui, HlAttrs attrs) int attr = ui->rgb ? attrs.rgb_ae_attr : attrs.cterm_ae_attr; bool bold = attr & HL_BOLD; bool italic = attr & HL_ITALIC; - bool reverse = attr & (HL_INVERSE | HL_STANDOUT); + bool reverse = attr & HL_INVERSE; + bool standout = attr & HL_STANDOUT; bool underline = attr & (HL_UNDERLINE), undercurl = attr & (HL_UNDERCURL); if (unibi_get_str(data->ut, unibi_set_attributes)) { @@ -478,6 +479,9 @@ static void update_attrs(UI *ui, HlAttrs attrs) if (underline || undercurl) { unibi_out(ui, unibi_enter_underline_mode); } + if (standout) { + unibi_out(ui, unibi_enter_standout_mode); + } if (reverse) { unibi_out(ui, unibi_enter_reverse_mode); } @@ -826,7 +830,7 @@ static void tui_cursor_goto(UI *ui, Integer row, Integer col) CursorShape tui_cursor_decode_shape(const char *shape_str) { - CursorShape shape = 0; + CursorShape shape; if (strequal(shape_str, "block")) { shape = SHAPE_BLOCK; } else if (strequal(shape_str, "vertical")) { @@ -834,7 +838,8 @@ CursorShape tui_cursor_decode_shape(const char *shape_str) } else if (strequal(shape_str, "horizontal")) { shape = SHAPE_HOR; } else { - EMSG2(_(e_invarg2), shape_str); + WLOG("Unknown shape value '%s'", shape_str); + shape = SHAPE_BLOCK; } return shape; } @@ -920,7 +925,6 @@ static void tui_set_mode(UI *ui, ModeShape mode) } TUIData *data = ui->data; cursorentry_T c = data->cursor_shapes[mode]; - int shape = c.shape; if (c.id != 0 && ui->rgb) { int attr = syn_id2attr(c.id); @@ -931,11 +935,12 @@ static void tui_set_mode(UI *ui, ModeShape mode) } } - switch (shape) { + int shape; + switch (c.shape) { + default: abort(); break; case SHAPE_BLOCK: shape = 1; break; case SHAPE_HOR: shape = 3; break; case SHAPE_VER: shape = 5; break; - default: WLOG("Unknown shape value %d", shape); break; } UNIBI_SET_NUM_VAR(data->params[0], shape + (int)(c.blinkon == 0)); unibi_out_ext(ui, data->unibi_ext.set_cursor_style); diff --git a/src/nvim/ui.c b/src/nvim/ui.c index c70a02d960..42366fdb76 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -186,6 +186,10 @@ Dictionary hlattrs2dict(const HlAttrs *aep, bool use_rgb) PUT(hl, "bold", BOOLEAN_OBJ(true)); } + if (mask & HL_STANDOUT) { + PUT(hl, "standout", BOOLEAN_OBJ(true)); + } + if (mask & HL_UNDERLINE) { PUT(hl, "underline", BOOLEAN_OBJ(true)); } @@ -198,7 +202,7 @@ Dictionary hlattrs2dict(const HlAttrs *aep, bool use_rgb) PUT(hl, "italic", BOOLEAN_OBJ(true)); } - if (mask & (HL_INVERSE | HL_STANDOUT)) { + if (mask & HL_INVERSE) { PUT(hl, "reverse", BOOLEAN_OBJ(true)); } diff --git a/src/nvim/viml/parser/expressions.c b/src/nvim/viml/parser/expressions.c index 4196ecb9d2..ee59dc8c96 100644 --- a/src/nvim/viml/parser/expressions.c +++ b/src/nvim/viml/parser/expressions.c @@ -779,7 +779,8 @@ const char *viml_pexpr_repr_token(const ParserState *const pstate, eltkn_opt_scope_tab[token.data.opt.scope], (int)token.data.opt.len, token.data.opt.name) TKNARGS(kExprLexPlainIdentifier, "(scope=%s,autoload=%i)", - intchar2str(token.data.var.scope), (int)token.data.var.autoload) + intchar2str((int)token.data.var.scope), + (int)token.data.var.autoload) TKNARGS(kExprLexNumber, "(is_float=%i,base=%i,val=%lg)", (int)token.data.num.is_float, (int)token.data.num.base, |